在上一篇介绍非阻塞IO时,我们可以认识到,非阻塞IO在条件不成立时直接返回。
通常我们在设置接口是阻塞还是非阻塞的时候,有两种方案:
(1)将文件描述符设置为非阻塞式文件描述符;
(2)通过传递特殊选项,让接口本身以非阻塞方式调用。
- fcntl
一个文件描述符,默认都是阻塞IO。
fcntl的函数原型如下:
fcntl可以改变已经打开的文件性质。针对cmd的值,fcntl能够接受第三个参数arg(可变参数列表)。传入的cmd的值不同,后面追加的参数也不相同。
fcntl函数有五种功能:
(1)复制一个现有的描述符(cmd = F_DUPFD);
(2)获得/设置文件描述符标记(cmd = FGETFD 或 FSETFD)
(3)获得/设置文件状态标记(cmd = FGETFL 或 FSETFL)
(4)获得/设置异步I/O所有权(cmd = FGETOWN 或 FSETOWN)
(5)获得/设置记录锁(cmd = FGETLK 或 FSETKW)
我们此处只是使用第三种功能,获得/设置文件状态标记,这样就可以将一个文件描述符设置为非阻塞。
- 实现函数SetNoBlock
基于fcntl,我们实现一个SetNoBlock函数,将文件描述符设置为非阻塞。
#include <unistd.h> #include<fcntl.h> void SetNoBlock(int fd){ int fl = fcntl(fd,F_GETFL);//使用F_GETFL将当前的文件描述符的属性取出来(这是一个位图) if(fl < 0){ perror("fcntl"); return; } fcntl(fd,F_SETFL,fl | O_NONBLOCK);//再使用F_SETFL将文件描述符设置回去,设置回去的同时,加上一个O_NONBLOCK参数 return; }
- 轮询方式读取标准输入
#include<stdio.h> #include <unistd.h> #include<fcntl.h> #include<errno.h> void SetNoBlock(int fd){ int fl = fcntl(fd,F_GETFL); if(fl < 0){ perror("fcntl"); return; } fcntl(fd,F_SETFL,fl | O_NONBLOCK); return; } int main(){ SetNoBlock(0);//将标准输入设为非阻塞 while(1){ char buf[1024] = {0}; ssize_t num = read(0,buf,sizeof(buf)-1); if(num == -1 && errno == EAGAIN){ printf("stdin data is not ready\n"); sleep(1); continue; } printf("input:%s\n",buf); } return 0; }
这个程序里的EAGAIN的定义如下: