通常來說,從普通文件讀數據,無論你是采用 fscanf,fgets 也好,read 也好,一定會在有限的時間內返回。但是如果你從設備,比如終端(標准輸入設備)讀數據,只要沒有遇到換行符(‘\n’),read 一定會“堵”在那而不返回。還有比如從網絡讀數據,如果網絡一直沒有數據到來,read 函數也會一直堵在那而不返回。
read 的這種行為,稱之為 block,一旦發生 block,本進程將會被操作系統投入睡眠,直到等待的事件發生了(比如有數據到來),進程才會被喚醒。
系統調用 write 同樣有可能被阻塞,比如向網絡寫入數據,如果對方一直不接收,本端的緩沖區一旦被寫滿,就會被阻塞。
比如下面的程序
int main() {
char buf[10];
int len;
while(1) {
// STDIN_FILENO 是標准輸入的描述符,它的值是 0. STDOUT_FILENO 是標准輸出的描述符,它的值是 1.
len = read(STDIN_FILENO, buf, 10);
write(STDOUT_FILENO, buf, len);
}
return 0;
}
gcc –o main.out main.c
./main.out
如果不向終端輸入數據,程序將永遠阻塞在read系統調用處。要規避這個問題,我們就需要用到非阻塞的IO。
對於一個給定的描述符有兩種方法對其指定非阻塞I/O:
1) 如果調用open獲得描述符,則可指定O_NONBLOCK標志
2) 對於已打開的一個描述符,則可調用fcntl,由該函數打開O_NONBLOCK文件狀態標志。
程序如下:
set_f1是設置讀取的方式為非阻塞,clr_f1則是清除非阻塞的方式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
void set_f1(int fd,int flags){
int val;
if ((val=fcntl(fd,F_GETFL,0)) < 0)
printf("fcntl F_GETFL error");
val|=flags;
if (fcntl(fd,F_SETFL,val) < 0)
printf("fcntl F_sETFL error");
}
void clr_f1(int fd,int flags){
int val;
if ((val=fcntl(fd,F_GETFL,0)) < 0)
printf("fcntl F_GETFL error");
val&=~flags;
if (fcntl(fd,F_SETFL,val) < 0)
printf("fcntl F_sETFL error");
}
char buf[50000];
int main()
{
int ntowrite,nwrite;
char *ptr;
ntowrite=read(STDIN_FILENO,buf,sizeof(buf));
fprintf(stderr,"read %d bytes\n",ntowrite);
set_f1(STDOUT_FILENO,O_NONBLOCK);
ptr=buf;
while(ntowrite > 0){
errno=0;
nwrite=write(STDOUT_FILENO,ptr,ntowrite);
fprintf(stderr,"nwrite=%d,errno=%d\n",nwrite,errno);
if(nwrite > 0){
ptr+=nwrite;
ntowrite-=nwrite;
}
}
clr_f1(STDOUT_FILENO,O_NONBLOCK);
return 0;
}
若標准輸出是普通文件,則可以期望write只執行一次
root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# ./main.out < /etc/services > temp.file
read 19605 bytes
nwrite=19605,errno=0
root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# ls -al temp.file
-rw-r--r-- 1 root root 19605 8月 12 15:33 temp.file
但是,如果標准輸出是終端。則有時會返回錯誤。
root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# cat stderr.out
read 19605 bytes
nwrite=14907,errno=0
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=-1,errno=11
nwrite=4698,errno=0
程序發出了多個write調用,但是只有部分真正輸出了數據。其他的都只返回了錯誤。這種形式的循環成為輪詢。在多用戶系統上會浪費CPU時間。