免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 8495 | 回复: 9
打印 上一主题 下一主题

异步IO读写管道文件,fcntl设置NONBLOCK并没有产生SIGIO,为什么? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-11-24 16:12 |只看该作者 |倒序浏览
我写了个小程序,创建一个FIFO文件,然后看看能否用异步IO来进行读写操作。流程是:
1. 父进程设置signal处理函数,打开这个fifo用来读+fcntl设置O_NONBLOCK模式。调用read函数,睡眠4s。
2. 子进程先sleep 2s,然后write到这个fifo。

我期待的结果是: 在父进程睡眠的时间里面,子进程write动作触发了父进程的异步read完成,并由此一个SIGIO信号。父进程捕捉此信号,在处理函数里面把读取的内容打印出来。

现在的问题是:
1. read函数并没有立刻return,而是等待子进程write结束。难道O_NONBLOCK模式没有起到作用? 这个O_NONBLOCK和O_ASYNC有什么区别啊?
2. 父进程没有捕捉到SIGIO信号,没有调用f()函数。

程序的执行结果如下,不是我预期的执行顺序:
> ./a.out
step3: child begin
step4: write ok
step1: begin async read
step2: read done
step6: father wake up

源代码:
  1. #include<sys/types.h>
  2. #include<sys/stat.h>
  3. #include<fcntl.h>
  4. #include<stdio.h>
  5. #include<stdlib.h>
  6. #include<unistd.h>
  7. #include<signal.h>
  8. char buf[6]={0};
  9. int fd;
  10. extern "C" void f(int signo){
  11.   printf("step5: 得到信号,读取内容=%s\n",buf);
  12. }
  13. int main(void){
  14.   char FIFO[]="myfifo";
  15.   unlink(FIFO);
  16.   mkfifo(FIFO,0666);
  17.   pid_t p=fork();
  18.   if(p==0){//child
  19.     sleep(2);
  20.     printf("step3: child begin\n");
  21.     fd=open(FIFO,O_WRONLY);
  22.     write(fd,"hello",5);
  23.     printf("step4: write ok\n");
  24.     close(fd);
  25.   }else if(p>0){//father
  26.     signal(SIGIO,f);
  27.     fd=open(FIFO,O_RDONLY);
  28.     int flags=fcntl(fd,F_GETFL);
  29.     if(flags<0){
  30.       printf("fcntl get failed\n");
  31.       return 1;
  32.     }
  33.     if(fcntl(fd,F_SETFL,flags|O_NONBLOCK)==-1){
  34.       printf("fcntl set failed\n");
  35.       return 1;
  36.     }
  37.     printf("step1: begin async read\n");
  38.     read(fd,buf,sizeof(buf));
  39.     printf("step2: read done\n");
  40.     sleep(4);
  41.     printf("step6: father wake up\n");
  42.     close(fd);
  43.   }else{}//error
  44.   return 0;
  45. }
复制代码

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
2 [报告]
发表于 2010-11-24 16:51 |只看该作者
NONBLOCK跟异步IO两码事

论坛徽章:
0
3 [报告]
发表于 2010-11-24 17:10 |只看该作者
  1. #include<sys/types.h>
  2. #include<sys/stat.h>
  3. #include<fcntl.h>
  4. #include<stdio.h>
  5. #include<stdlib.h>
  6. #include<unistd.h>
  7. #include<signal.h>
  8. char buf[6]={0};
  9. int fd;
  10. void f(int signo){
  11.   printf("step5: 得到信号,读取内容=%s\n",buf);
  12. }
  13. int main(void){
  14.   char FIFO[]="myfifo";
  15.   unlink(FIFO);
  16.   mkfifo(FIFO,0666);
  17.     signal(SIGIO,f);
  18.     fd=open(FIFO,O_RDWR);
  19.         fcntl(fd,F_SETOWN,getpid());
  20.     int flags=fcntl(fd,F_GETFL);
  21.     if(flags<0){
  22.       printf("fcntl get failed\n");
  23.       return 1;
  24.     }
  25.     if(fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC)==-1){
  26.       printf("fcntl set failed\n");
  27.       return 1;
  28.     }
  29.   pid_t p=fork();
  30.   if(p==0){//child
  31.     sleep(2);
  32.     printf("step3: child begin\n");
  33.    write(fd,"hello",5);
  34.     printf("step4: write ok\n");
  35.     close(fd);
  36.   }else if(p>0){//father
  37.     printf("step1: begin async read\n");
  38.     read(fd,buf,sizeof(buf));
  39.     printf("step2: read done\n");
  40.     sleep(4);
  41.     printf("step6: father wake up\n");
  42.     close(fd);
  43.   }else{}//error
  44.   return 0;
  45. }
复制代码
帮你改了下
gcc -o pipetest main.c
一次输出12345

记住open的时候不要加O_ASYNC 不然默认的SIGIO没了。

论坛徽章:
0
4 [报告]
发表于 2010-11-24 17:20 |只看该作者
O_NONBLOCK其实只是个flag而已,用户态应用程序设置了,
比如调用read, 但该文件没内容, 就立即返回, 返回不返回还要基于内核态相应的设置,比如:
内核会有一段程序:
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
如果设置O_NONBLOCK,就返回,
不然没数据就睡眠
等待数据来临

论坛徽章:
0
5 [报告]
发表于 2010-11-24 17:29 |只看该作者
帮你改了下
gcc -o pipetest main.c
一次输出12345

记住open的时候不要加O_ASYNC 不然默认的SIGIO没了 ...
威廉爵爺 发表于 2010-11-24 17:10


非常感谢你的答复。
在我的solaris/aix环境下,我现在的运行结果是12346,但是没有5,也就是SIGIO仍然没有被捕捉

没有得到SIGIO的问题还是存在的。不知道大虾你是什么环境?

还有两个问题:
1.fcntl(fd,F_SETOWN,getpid());这句话一定要加么? 我去掉以后发现也没有什么变化。
2.为什么要加上FASYNC? 前面那位大虾说的,非阻塞和异步是两回事,那么同步状态下的NON_BLOCK标志,有什么意义么? 什么时候需要同步+非阻塞?
3.我发现,按照我原来的写法,父子进程都去open这个文件,然后再fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC),还是不能得到正确的顺序。而像你这样先open了,然后再fork操作,顺序才是对的。这是为什么?

我现在改成了这样,顺序还是不对:
  1. #include<sys/types.h>
  2. #include<sys/stat.h>
  3. #include<fcntl.h>
  4. #include<stdio.h>
  5. #include<stdlib.h>
  6. #include<unistd.h>
  7. #include<signal.h>
  8. char buf[6]={0};
  9. int fd;
  10. extern "C" void f(int signo){
  11.   printf("step5: read content=%s\n",buf);
  12. }
  13. int main(void){
  14.   char FIFO[]="myfifo";
  15.   unlink(FIFO);
  16.   mkfifo(FIFO,0666);
  17.   pid_t p=fork();
  18.   if(p==0){//child
  19.     sleep(2);
  20.     printf("step3: child begin\n");
  21.     fd=open(FIFO,O_WRONLY);
  22.     int flags=fcntl(fd,F_GETFL);
  23.     if(flags<0){
  24.       printf("fcntl get failed\n");
  25.       return 1;
  26.     }
  27.     if(fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC)==-1){
  28.       printf("fcntl set failed\n");
  29.       return 1;
  30.     }
  31.     write(fd,"hello",5);
  32.     printf("step4: write ok\n");
  33.     close(fd);
  34.   }else if(p>0){//father
  35.     signal(SIGIO,f);
  36.     fd=open(FIFO,O_RDONLY);
  37.     int flags=fcntl(fd,F_GETFL);
  38.     if(flags<0){
  39.       printf("fcntl get failed\n");
  40.       return 1;
  41.     }
  42.     if(fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC)==-1){
  43.       printf("fcntl set failed\n");
  44.       return 1;
  45.     }
  46.     printf("step1: begin async read\n");
  47.     read(fd,buf,sizeof(buf));
  48.     printf("step2: read done\n");
  49.     sleep(4);
  50.     printf("step6: father wake up\n");
  51.     close(fd);
  52.   }else{}//error
  53.   return 0;
  54. }
复制代码

论坛徽章:
0
6 [报告]
发表于 2010-11-24 17:32 |只看该作者
回复 5# kgisme170


    fedora 啊, 你用我的代码  出不来5? 我不信了? 一个字不改看行不

论坛徽章:
0
7 [报告]
发表于 2010-11-24 17:34 |只看该作者
fd 不是 共享的吗  为什么要打开2次

论坛徽章:
0
8 [报告]
发表于 2010-11-24 17:36 |只看该作者
fcntl(fd,F_SETOWN,getpid())

这步 是让内核知道把时间通知谁,

论坛徽章:
0
9 [报告]
发表于 2010-11-24 17:43 |只看该作者
你知道为什么按照你的代码 3(sleep2秒) 会在 1之前输出吗
因为fd=open(FIFO,O_RDONLY); 在write之前是一直不返回,被阻塞住的,所以3 会在1之前输出
所以统一的把mode改成O_RDWR
然后再把fcntl(fd, F_SETOWN, getpid( ));加进去,
修改后代码如下, 你再try下
  1.     #include<sys/types.h>
  2.     #include<sys/stat.h>
  3.     #include<fcntl.h>
  4.     #include<stdio.h>
  5.     #include<stdlib.h>
  6.     #include<unistd.h>
  7.     #include<signal.h>
  8.     char buf[6]={0};
  9.     int fd;
  10.     void f(int signo){
  11.       printf("step5: read content=%s\n",buf);
  12.     }
  13.     int main(void){
  14.       char FIFO[]="myfifo";
  15.       unlink(FIFO);
  16.       mkfifo(FIFO,0666);
  17.       pid_t p=fork();
  18.       if(p==0){//child
  19.         sleep(2);
  20.         printf("step3: child begin\n");
  21.         fd=open(FIFO,O_RDWR/*O_WRONLY*/);
  22.         int flags=fcntl(fd,F_GETFL);
  23.         if(flags<0){
  24.           printf("fcntl get failed\n");
  25.           return 1;
  26.         }
  27.         if(fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC)==-1){
  28.           printf("fcntl set failed\n");
  29.           return 1;
  30.         }
  31.         write(fd,"hello",5);
  32.         printf("step4: write ok\n");
  33.         close(fd);
  34.       }else if(p>0){//father
  35.         signal(SIGIO,f);
  36.         fd=open(FIFO,O_RDWR/*O_RDONLY*/);
  37.         fcntl(fd,F_SETOWN,getpid());
  38.         int flags=fcntl(fd,F_GETFL);
  39.         if(flags<0){
  40.           printf("fcntl get failed\n");
  41.           return 1;
  42.         }
  43.         if(fcntl(fd,F_SETFL,flags|O_NONBLOCK|FASYNC)==-1){
  44.           printf("fcntl set failed\n");
  45.           return 1;
  46.         }
  47.         printf("step1: begin async read\n");
  48.         read(fd,buf,sizeof(buf));
  49.         printf("step2: read done\n");
  50.         sleep(4);
  51.         printf("step6: father wake up\n");
  52.         close(fd);
  53.       }else{}//error
  54.       return 0;
  55.     }
复制代码

论坛徽章:
0
10 [报告]
发表于 2010-11-25 09:13 |只看该作者
你知道为什么按照你的代码 3(sleep2秒) 会在 1之前输出吗
因为fd=open(FIFO,O_RDONLY); 在write之前是一 ...
威廉爵爺 发表于 2010-11-24 17:43



    终于对了!非常的感谢啊!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP