Unix下共有五種I/O模型:
1. 阻塞式I/O
2. 非阻塞式I/O
3. I/O複用(select和poll)
4. 信號驅動式I/O(SIGIO)
5. 異步I/O(POSIX的aio_系列函數) 網絡
io請求分兩步:
1. 先將數據從存儲介質(磁盤,網絡等)拷貝到內核緩衝區,此時稱爲數據準備好,能夠被用戶應用程序讀取。
2. 由用戶應用程序拷貝內核緩衝區中的數據到用戶緩衝區。 異步
咱們將函數recvfrom視爲系統調用,不論該函數如何實現,通常都會有一個從應用進程中運行到內核中運行的切換,一段時間之後還會有一個返回到應用進程的切換。函數
應用程序調用一個IO函數,致使應用程序阻塞並等待數據準備就緒。若是數據沒有準備好,一直等待。若是數據準備好了,則從內核拷貝到用戶空間拷貝數據,IO函數返回成功指示。測試
進程調用recvfrom,此係統調用直到數據報到達且被複制到應用進程的緩衝區中或發生錯誤才返回,常見的錯誤如系統調用被信號中斷。spa
進程在調用recvfrom開始到它返回的整段時間內是被阻塞的,該函數成功返回後,應用進程開始處理數據報。操作系統
咱們把一個套接口設置爲非阻塞就是告訴內核,當所請求的I/O操做沒法完成時,不要將進程睡眠,而是返回一個錯誤。這樣咱們的I/O操做函數將不斷的測試 數據是否已經準備好,若是沒有準備好,繼續測試,直到數據準備好爲止。在這個不斷測試的過程當中,會大量的佔用CPU的時間。線程
前三次調用recvfrom時仍無數據返回,所以內核當即返回一個EWOULDBLOCK錯誤。第四次調用recvfrom時,數據報已準備好,被拷貝到應用緩衝區,recvfrom 返回成功指示,接着就是咱們處理數據。3d
當一個應用進程像這樣對一個非阻塞描述字循環調用recvfrom 時,咱們稱此過程爲輪詢(polling)。因爲應用進程像這樣接二連三地查詢內核,看看某操做是否準備好,這對CPU時間是極大的浪費,因此這種模型只是偶爾纔會遇到。指針
I/O複用模型會用到select或者poll函數,這兩個函數也會使進程阻塞,可是和阻塞I/O所不一樣的的,這兩個函數能夠同時阻塞多個I/O操做。並且能夠同時對多個讀操做,多個寫操做的I/O函數進行檢測,直到有數據可讀或可寫時,才真正調用I/O操做函數。 blog
只要有數據就緒,select調用返回,應用程序調用recvfrom將數據從內核區拷貝至用戶區。
仔細看實例圖,發現select模型彷佛有些disadvantage,即先後進行了兩次系統調用,比上一個模型多了一次。然而,select模型也有其明顯的優點:每次select阻塞結束返回後,能夠得到多個準備就緒的套接字(即一個select能夠對多個套接字進行管理,相似於同時監控多個套接字事件是否就緒)。
和阻塞IO模型相比,selectI/O複用模型至關於提早阻塞了。等到有數據到來時,再調用recv就不會由於要等數據就緒而發生阻塞。
咱們能夠用信號,讓內核在數據就緒時用信號SIGIO通知咱們,將此方法稱爲信號驅動I/O
首先,咱們容許套接字進行信號驅動I/O,並經過系統調用 sigaction 安裝一個信號處理程序。此係統調用當即返回,進程繼續工做,它是非阻塞的。當數據報準備好被讀時,就爲該進程生成一個SIGIO信號。咱們隨便可以在信號處理程序中調用 recvfrom 來取讀數據報。
咱們讓內核啓動操做,並在整個操做完成後(包括將數據從內核拷貝到咱們本身的緩衝區)通知咱們。
調用aio_read函數,告訴內核描述字,緩衝區指針,緩衝區大小,文件偏移以及通知的方式,而後當即返回。當內核將數據拷貝到緩衝區後,再通知應用程序。
前四種模型主要區別在第一階段,由於前四種模型的第二階段基本相同:在數據從內存拷貝到調用者的緩衝區時,進程阻塞於recvfrom 調用。然而,異步I/O模型處理的兩個階段都不一樣於前四個模型。
上述五種I/O模型,前四種均屬於同步I/O(它們等待方式不一樣,搬遷動做相同),由於recvfrom調用均阻塞了當前請求進程。
只有最後一種io屬於異步I/O !
所謂同步,數據從存儲介質拷貝到內核緩衝區(數據準備的過程)完成以後,須要用戶本身將數據拷貝到用戶緩衝區。
所謂異步,步驟1,2 用戶都不關心,只要發起IO請求,後面獲得IO結果便可。
因此,前4種IO模型都是同步的!!!
阻塞,非阻塞,同步,異步 歸納:
阻塞,非阻塞:進程/線程要訪問的數據是否就緒,進程/線程是否須要等待;
同步,異步:訪問數據的方式,同步須要主動讀寫數據,在讀寫數據的過程當中仍是會阻塞;異步只須要I/O操做完成的通知,並不主動讀寫數據,由操做系統內核完成數據的讀寫。