一、阻塞IO
- 在“系統調用中斷”文章中(javascript:void(0))我們将系統調用分為兩類:低速系統調用和其他系統調用
- 低速系統調用是可能會使程序永遠阻塞的一類系統調用,包括下面:
- 如果某些檔案類型(如讀管道、終端裝置和網絡裝置)的資料并不存在,讀操作可能會使調用者永遠阻塞
- 如果資料不能被相同的檔案類型立即接受(如管道中無空間、網絡流控制),寫操作可能會使調用者永遠阻塞
- 在某種條件發生之前打開某些檔案類型可能會發生阻塞(如要打開一個終端裝置,需要先等待與之連接配接的數據機應答,又如若以隻寫模式打開FIFO,那麼在沒有其他程序已用讀模式打開該FIFO時也要等待)
- 對已經加上強制性記錄鎖的檔案進行讀寫
- 某些ioctl操作
- 某些程序間通信函數
- 我們也曾說過,雖然讀寫磁盤檔案會暫時阻塞調用者,但并不能将與磁盤I/O有關的系統調用視為“低速”
二、非阻塞IO
- 概念:非阻塞I/O使我們可以調用不會永遠阻塞的I/O操作,例如open,read和write。如果這種操作不能完成,則立即出錯傳回,表示該操作如繼續執行将繼續阻塞下去
- 有兩種方法來設定非阻塞IO:
- 如果是調用open以獲得該描述符,則可指定O_NONBLOCK标志
- 對于已經打開的一個描述符,則可調用fcntl打開O_NONBLOCK檔案狀态标志
三、非阻塞IO案例
- 下面是一個非阻塞I/O的執行個體,它從标準輸入讀500000位元組,并試圖将它們寫到标準輸出上。該程式先将标準輸出設定為非阻塞的,然後用for循環進行輸出,每次write的結果都在标準出錯上列印
- 函數clr_fl清除檔案描述符标志,它清除1個或多個标志位
代碼如下
#include<errno.h> #include<fcntl.h> #include<stdlib.h> #include<stdio.h> char buf[500000]; void clr_fl(int fd, int flags); void set_fl(int fd,int flags); int main(void) { int ntowrite, nwrite; char *ptr; ntowrite = read(0, buf, sizeof(buf));//從标準輸入讀取資料 fprintf(stderr, "read %d bytes\n", ntowrite); set_fl(1, O_NONBLOCK); //設定标準輸出非阻塞 ptr = buf; while (ntowrite > 0) { errno = 0; nwrite = write(1, ptr, ntowrite);//将資料輸出到标準輸出上 fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno); if (nwrite > 0) { ptr += nwrite; ntowrite -= nwrite; } } clr_fl(2, O_NONBLOCK); //設定标準輸出阻塞 exit(0); } void clr_fl(int fd, int flags) { int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到 perror("fcntl F_GETFL error"); val &= ~ flags; /* turn off flags */ if (fcntl(fd, F_SETFL, val) < 0)//再設定 perror("fcntl F_SETFL error"); } void set_fl(int fd, int flags) { int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到 perror("fcntl F_GETFL error"); val |= flags; /* turn on flags */ if (fcntl(fd, F_SETFL, val) < 0)//再設定 perror("fcntl F_SETFL error"); }
- 示範①:例如我們從/etc/services檔案中讀取資料,然後将資料輸出到檔案temp.file檔案中,則write隻執行一次
- 示範②:我們從/etc/services檔案中讀取資料,然後将資料輸出到終端上,結果顯示write有時傳回小于50000的一個數字,有時傳回錯誤