上一篇博客我们以read终端设备为例介绍了非阻塞I/O,为什么我们不直接对STDIN_FILENO做非阻塞read,而要重新open一遍/dev/tty呢?因为STDIN_FILENO在程序启动时已经被自动打开了,而我们需要在调用open时指定O_NONBLOCK标志。这里介绍另外一种办法,可以用fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。头文件及函数原型:
1 #include <unistd.h> 2 #include <fcntl.h> 3 4 int fcntl(int fd, int cmd); 5 int fcntl(int fd, int cmd, long arg); 6 int fcntl(int fd, int cmd, struct flock *lock);
这个函数和open一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd参数。下面的例子使用F_GETFL和F_SETFL这两种fcntl命令改变STDIN_FILENO的属性,加上O_NONBLOCK选项,实现前一博客 “非阻塞读终端”同样的功能。
1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <iostream> 7 using namespace std; 8 9 #define MSG_TRY "try again " 10 11 int main(void) 12 { 13 char buf[10]; 14 int n; 15 int flags; 16 flags = fcntl(STDIN_FILENO, F_GETFL);//获取标准输入的标志位 17 flags |= O_NONBLOCK;//增加非阻塞状态标志 18 if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1)//写回标准输入标志位 19 { 20 cout << "fcntl" << endl; 21 return 1; 22 } 23 tryagain: 24 n = read(STDIN_FILENO, buf, 10); 25 if (n < 0) { 26 if (errno == EAGAIN) { 27 sleep(1); 28 write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY)); 29 goto tryagain; 30 } 31 cout << "read stdin" << endl; 32 return 1; 33 } 34 write(STDOUT_FILENO, buf, n); 35 return 0; 36 }