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,此系統調用直到數據報到達且被復制到應用進程的緩沖區中或發生錯誤才返回,常見的錯誤如系統調用被信號中斷。
進程在調用recvfrom開始到它返回的整段時間內是被阻塞的,該函數成功返回後,應用進程開始處理數據報。
我們把一個套接口設置為非阻塞就是告訴內核,當所請求的I/O操作無法完成時,不要將進程睡眠,而是返回一個錯誤。這樣我們的I/O操作函數將不斷的測試 數據是否已經准備好,如果沒有准備好,繼續測試,直到數據准備好為止。在這個不斷測試的過程中,會大量的占用CPU的時間。
前三次調用recvfrom時仍無數據返回,因此內核立即返回一個EWOULDBLOCK錯誤。第四次調用recvfrom時,數據報已准備好,被拷貝到應用緩沖區,recvfrom 返回成功指示,接著就是我們處理數據。
當一個應用進程像這樣對一個非阻塞描述字循環調用recvfrom 時,我們稱此過程為輪詢(polling)。由於應用進程像這樣連續不斷地查詢內核,看看某操作是否准備好,這對CPU時間是極大的浪費,所以這種模型只是偶爾才會遇到。
I/O復用模型會用到select或者poll函數,這兩個函數也會使進程阻塞,但是和阻塞I/O所不同的的,這兩個函數可以同時阻塞多個I/O操作。而且可以同時對多個讀操作,多個寫操作的I/O函數進行檢測,直到有數據可讀或可寫時,才真正調用I/O操作函數。
只要有數據就緒,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操作完成的通知,並不主動讀寫數據,由操作系統內核完成數據的讀寫。
http://xxxxxx/Linuxjc/1145359.html TechArticle