先前以read终端设备为例介绍了非阻塞I/O,因为STDIN_FILENO在程序启动时已经被自动打开了,所以我们需要调用open打开“/dev/tty”设备,并指定O_NONBLOCK标志。链接如下:
阻塞与非阻塞_weixin_38011893的博客-CSDN博客
现在,可以用fcntl函数改变一个已打开的文件的属性而不必重新open文件,可以重新设置读、写、追加、非阻塞等标志。
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
除了F_GETFL和F_SETFL命令之外,fcntl还有很多命令做其他操作,例如设置文件记录锁等。可以通过fcntl设置的都是当前进程如何访问设备或文件的访问控制属性。
非阻塞读标准输入,代码演示如下:
#include "./common/head.h" /*功能: *用fcntl函数实现非阻塞I/O,从标准输入读取数据到标准输出。 *思路:先读取打开文件的属性flag,加上想要的属性,再设置回去。 */ int main() { int flags; if( (flags = fcntl(STDIN_FILENO, F_GETFL)) < 0 ){ //获取标准输入文件属性 perror("fcntl get flags"); exit(1); } flags |= O_NONBLOCK; //加上非阻塞属性 if( (flags = fcntl(STDIN_FILENO, F_SETFL, flags)) < 0){ //将新属性设置回去 perror("fcntl set flags"); exit(1); } int cnt = 10; char buff[20] = {0}; while(cnt--){ //轮询10次 ssize_t n = read(STDIN_FILENO, buff, 10); if(n >= 0){ //读到了 printf("read %ld bytes\n", n); write(STDOUT_FILENO, buff, n); //将读到的数据打印到标准输出 break; } if(errno != EAGAIN){ //其他错误 perror("read STDIN_FILENO"); eixt(1); } write(STDOUT_FILENO, "try again?\n", 11); sleep(1); } return 0; }
获取标准输入、标准输出、标准错误文件默认访问控制属性,代码演示如下:
#include "./common/head.h" /*功能: *执行./a.out 0或执行./a.out 1或执行./a.out 2 *获取标准输入、标准输出、标准错误这三个文件的访问控制属性 */ int main(int argc, char *argv[]) { if(argc != 2){ printf("usage:cmd fd\n"); return 1; } #if 0 //各个标志属性的值。ps:%#x格式打印,数值前面加上0x printf("O_RDONLY = %#x\n", O_RDONLY); //0x00 printf("O_WRONLY = %#x\n", O_WRONLY); //0x01 printf("O_RDWR = %#x\n", O_RDWR); //0x02 printf("O_ACCMODE = %#x\n", O_ACCMODE); //0x03,正好是上面3种的组合 printf("O_APPEND = %#x\n", O_APPEND); //0x400 printf("O_RDONLY = %#x\n", O_NONBLOCK); //0x800 #endif int flags; if( (flags = fcntl(atoi(argv[1]), F_GETFL)) < 0){ //atoi函数,将ascii码转换为数值 perror("fcntl get flags"); exit(1); } switch(flags & O_ACCMODE) //O_ACCMODE宏的值为0x03, 最低两位00、01、10刚好代表只读、只写、读写,和flags作与操作,即可得到这三种状态 { case O_RDONLY: printf("%s fd : read only", argv[1]); break; case O_WRONLY: printf("%s fd : write only", argv[1]); break; case O_RDWR: printf("%s fd : read write", argv[1]); break; defalut: printf("invalid access mode\n"); break; } //flags和其他标志做&操作,不为0就代表该文件有此标志的访问控制属性 if(flags & O_NONBLOCK) printf(", nonblock"); if(flags & O_APPEND) printf(", append"); putchar(10); //最后输出换行 return 0; }
结果:
$ ./a.out 0
$ 0 fd : read write
$ ./a.out 1
$ 1 fd : read write
$ ./a.out 2
$ 2 fd : read write