linux c编程:非阻塞IO

mac2022-06-30  89

通常来说,从普通文件读数据,无论你是采用 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 串口非阻塞读取与发送
最新回复(0)