通常来说,从普通文件读数据,无论你是采用 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时间。
转载于:https://www.cnblogs.com/zhanghongfeng/p/9463236.html
相关资源:linux gcc tty 串口非阻塞读取与发送