多路復用
函數說明
我們都知道fcntl()函數解決了文件共享的問題,如果不知道請看: http://blog.csdn.net/mybelief321/article/details/8993138。接下來該處理I/O復用的情況了。那麼,什 麼是I/O復用呢?這個咱得先弄清楚,所謂的I/O復用無非就是多個進程共同使用一個I/O輸入輸出流。
在經典的《Unix網絡編程第1卷》Chapter 6中作者詳細介紹了五種I/O模型,分別為:
① blocking I/O
② nonblocking I/O
③ I/O multiplexing (select and poll)
④ signal driven I/O (SIGIO)
⑤ asynchronous I/O (the POSIX aio_functions)
我英語水平一般,還是換成中文的來 說吧。I/O處理的模型有5種:
① 阻塞I/O型:在這種模式下,若所調用的I/O函數沒有完成相關的功能 ,則會使進程掛起,直到相關數據到達才會返回。如常見對管道設備、終端設備和網絡設備進行讀寫時經常會 出現這種情況。
② 非阻塞I/O型:在這種模式下,當請求的I/O操作不能完成時,則不讓進程睡眠,而 且立即返回。非阻塞I/O使用戶可以調用不會阻塞的I/O操作,如open()、write()和read()。如果該操作不能 完成,則會立即返回出錯(如打不開文件)或者返回0(比如在緩沖區中沒有數據可以讀取或者沒有空間可以 寫入數據)。
③ I/O多路轉接模型:在這種模式下,如果請求的I/O操作阻塞,且它不是真正的阻塞 I/O,而是讓其中的一個函數等待,在此期間,I/O還能進行其他操作。咱們接下來要說的select()函數和 poll()函數,就是屬於這種類型。
④ 信號驅動I/O模型:在這種模式下,進程要定義一個信號處理程 序,系統可以自動捕獲特定信號的到來,從而啟動I/O。這是由內核通知用戶何時可以啟動一個I/O操作決定的 。這種模型是非阻塞的,當有就緒的數據時,內核就向該進程發送SIGIO信號。無論我們如何處理SIGIO信號, 這種模型的好處就是當等待數據到達時,可以不阻塞。主程序繼續執行,只有收到SIGIO信號時,才去處理數 據即可。
⑤ 異步I/O模型:在這種模型下,進程先讓內核啟動I/O操作,並在整個操作完成後通知該進 程。這種模型與信號驅動模型的主要區別在於:信號驅動I/O是由內核通知我們何時可以啟動一個I/O操作,而 異步I/O模型是由內核通知進程I/O操作何時完成的。現在並不是所有的系統都支持這種模型。
相比較 而言,select()和poll()的I/O多路轉換模型是處理I/O復用的一個高效的方法。它可以具體設置程序中每一個 所關心的文件描述符的條件、希望等待的時間等,從select()函數和poll()函數返回時,內核會通知用戶已准 備好的文件描述符的數量、已准備好的條件(或事件)等。通過使用select()和poll()函數的返回結果(可能 是檢測到某個文件描述符的注冊事件或是超時,或是調用出錯),就可以調用相應的I/O處理函數了。
函數格式
下圖為select()函數的介紹
可以看到,select函數根據希望進行的文件 操作對文件描述符進行了分類,這裡對文件描述符的處理主要涉及4個宏函數,如下表2所示
另外,select()函數中的 timeou 是一個 struct timeval類型的指針,該結構體如下所示:
可以看到,這個時間結構體的精確度可以設 置到微秒級,這對於大多數的應用而言已經足夠了。
使用select()函數的過程可概括為:先調用 FD_ZERO() 將指定的fd_set清零,然後調用宏FD_SET()將需要測試的fd加入fd_set,接著調用函數select測試 fd_set中的所有fd,最後用用宏FD_ISSET()檢查某個fd在函數select調用後,相應位是否仍然為1。在執行完 對相關文件描述符的操作後,使用FD_CLR來描述符集。
下表為 poll()函數的介紹:
基礎實驗
實驗原理
本實驗主 要實現通過調用poll()函數來監聽三個終端的輸入(分別重定向到兩個管道文件的虛擬終端及主程序所運行的 虛擬終端)並分別進行相應的處理。在這裡我們建立了一個poll()函數監聽的讀文件描述符集,其中包含三個 文件描述符,分別為標准輸入文件描述符和兩個管道文件描述符。通過監視主程序的虛擬終端標准輸入來實現 程序的控制(如程序結束);以兩個管道作為數據輸入,主程序將從兩個管道讀取的輸入字符串寫入到標准輸 出文件(屏幕)。
為了充分表現poll()函數的功能,在運行主程序時,需要打開3個虛擬終端:首先 用mknod命令創建兩個管道 in1 和 in2。接下來,在兩個虛擬終端上分別運行 cat > in1和cat > in2. 同時在第三個虛擬終端上運行主程序。
在程序運行後,如果在兩個管道終端上輸入字符串,則可以觀 察到同樣的內容將在主程序的虛擬終端上逐行顯示。
如果想結束主程序,只要在主程序的虛擬終端下 輸入以“q”或“Q”字符開頭的字符串即可。如果三個文件一直在無輸入狀態張,則主程序一直處於阻塞狀態 。為了防止無限期的阻塞,在程序中設置超時值(本實驗匯總設置為60s),當無輸入狀態持續到超時值時, 主程序主動結束運行並退出。
程序的流程圖如下:
實驗步驟:本次實驗只需要一個c文件,即 multiplex_poll.c,我上傳到了網站,請自行下載:點此下載
在第一個終端中,使用命令:gcc multiplex.c -o multiplex編譯文件,如下
使用命令建立兩個管道文件:mknod in1 p
mknod in2 p,如下圖,mknod命令不了解的請點此
在終端1執行命 令:./multiplex_select
再打開兩個終端,在終端2中執行命令: cat > in1 ,不了解cat命令的 請點此,在終端3中執行命令:cat > in2,如下圖所示,分別在終端2和3中輸入字符,終端1中就會顯示, 在終端1中輸入q或Q,則立刻結束程序運行。
下圖為程序的超時結果: