在linux系統中,i/o操作可以分為兩種模式:阻塞式i/o和非阻塞式i/o。
這兩種模式決定了進程在執行I/O操作時的行為方式,以及CPU資源的利用效率。

阻塞的本質是進程在無法完成某個操作時,進入休眠狀態,交出了CPU控制權,等待操作條件滿足再被喚醒執行。
這種情況下,進程會被掛起,暫停執行其他任務。
例如,像wait()、pause()、sleep()等函數都會導致進程進入阻塞狀態。
非阻塞則是指即使操作條件尚未滿足,進程也不會等待,而是立刻返回控制權并繼續執行其他任務。
1
阻塞式 I/O (Blocking I/O)
阻塞式I/O是默認的I/O操作模式。它的典型特點是,當對文件或設備進行讀寫操作時,如果資源當前不可用,操作系統會掛起調用者,直到資源變得可用。
例如:
對于普通文件(如文本文件),即使讀寫的數據量較大,read()或write()總會在有限時間內返回。這是因為普通文件的I/O操作不會阻塞。對于特殊類型的文件,如管道文件、網絡套接字和字符設備文件,如果在讀取數據時文件無數據可讀,進程會被阻塞,直到有數據可用時才被喚醒。這種行為能夠提升系統的響應效率,因為當條件不滿足時,進程進入阻塞狀態,CPU資源可以被其他進程利用。
2
非阻塞式 I/O (Non-blocking I/O)
非阻塞I/O則是在執行I/O操作時,不管資源是否可用,操作系統都不會讓進程進入阻塞狀態,而是立即返回控制權。
通常返回一個特殊的錯誤碼(如EAGaiN),表示當前操作未能完成,資源暫時不可用。
非阻塞I/O一般需要輪詢資源狀態或使用異步事件通知來檢測資源是否變得可用。
3
阻塞與非阻塞 I/O 的區別
舉個例子,假設程序嘗試從管道文件或網絡套接字中讀取數據:
阻塞式 I/O:如果當前沒有數據可讀,調用read()函數時,進程會被掛起,直到有數據寫入管道或網絡緩沖區為止。這段時間,CPU可以分配給其他進程使用。非阻塞式 I/O:即使沒有數據可讀,read()函數也會立即返回,并設置errno為EAGAIN或EWOULDBLOCK。進程可以選擇繼續執行其他任務,或者通過輪詢方式不斷嘗試讀取,直到數據可用。
4
阻塞與非阻塞 I/O 的優缺點
阻塞式 I/O 優點:
程序結構簡單,不需要處理I/O狀態的變化。在I/O等待時,能夠讓出CPU資源,提高系統整體的CPU利用效率。
阻塞式 I/O 缺點:
由于進程可能長時間阻塞,會降低系統的響應性。不適合高并發場景,因為每個阻塞的進程都會占用一個線程或進程資源。
非阻塞式 I/O 優點:
提高響應性,特別適用于需要實時交互的場景。適合高并發服務器程序,如網絡服務器,使用非阻塞I/O可以高效處理大量請求。
非阻塞式 I/O 缺點:程序復雜度增加,需要管理I/O狀態和輪詢操作。
如果沒有采用合適的等待機制(如select、poll或epoll),可能會導致CPU資源被占用過多。
以鼠標輸入設備文件為例,Linux中鼠標對應的設備文件通常位于/dev/input/目錄下,命名為mouseX(X為序號)或eventX。

例如,使用od命令查看/dev/input/event3設備的數據:
代碼語言:JavaScript代碼運行次數:0運行復制
sudo od -x /dev/input/event3
在終端中移動鼠標或點擊按鈕時,會輸出相應的數據。
如果沒有輸出,說明該設備文件不是鼠標對應的設備。

編寫一個測試程序,使用阻塞式I/O讀取鼠標的輸入:
代碼語言:javascript代碼運行次數:0運行復制
int main() { int fd = open("/dev/input/event3", O_RDONLY); if (fd == -1) { perror("Error opening device"); return 1; } char buffer[128]; printf("Reading from mouse device...n"); while (1) { // 阻塞等待,直到有數據可讀 int bytes_read = read(fd, buffer, sizeof(buffer)); if (bytes_read > 0) { printf("Read %d bytes from the mousen", bytes_read); } } close(fd); return 0;}
在這個程序中,如果鼠標沒有輸入數據,read()函數會阻塞,進程進入休眠狀態,直到檢測到鼠標動作才繼續執行。
修改程序,使其以非阻塞方式讀取鼠標設備數據:
代碼語言:javascript代碼運行次數:0運行復制
int main() { int fd = open("/dev/input/event3", O_RDONLY | O_NONBLOCK); if (fd == -1) { perror("Error opening device"); return 1; } char buffer[128]; printf("Reading from mouse device (non-blocking)...n"); while (1) { int bytes_read = read(fd, buffer, sizeof(buffer)); if (bytes_read > 0) { printf("Read %d bytes from the mousen", bytes_read); } else if (bytes_read == -1 && errno == EAGAIN) { // 沒有數據可讀,非阻塞操作立刻返回 printf("No data available, will try again...n"); } // 稍微休眠一下,避免CPU資源過度消耗 usleep(100000); } close(fd); return 0;}
在這個程序中,即使沒有鼠標數據,read()也會立即返回,并設置errno為EAGAIN。
此時程序不會被掛起,而是可以繼續嘗試讀取或執行其他任務。