FIFO中的O_NONBLOCK标志的影响(非阻塞I/O)

FIFO(命名管道)是一种文件类型。所以可以用open()函数来打开它。O_NONBLOCK就是和FIFO息息相关的一个打开标志。

我们可以用mkdifo(const char *pathname ,mode_t mode)
来创建一个FIFO,然后就可以用opne()函数来打开了。
当打开一个FIFO是 O_NONBLOCK 产生的影响如下:
    1若没有这个标志,以只读方式打开的FIFO要阻塞到其他的某个程序以写打开这个FIFO。同样以只写方式打开的FIFO要阻塞到其他某个进程以读方式打开该FIFO。
   2若指定了这个标志,则以只读方式打开会立刻返回而不阻塞(不是出错返回)。而以只写方式打开,若之前没有进程以读方式打开这个FIFO则立刻出错返回。

好奇的人会想,如果我以读写方式打开会怎么样呢。我在自己测试了下 以读写方式打开FIFO  无论是否指定了O_NONBLOCK 标志 open函数都立刻成功返回,这里也容易理解
以为因为他本身就以两读写式打开了管道的读写两端,所以也就不需要别的进程以读或写方式来打开他没有打开的另一端。不过,历史上管道都是半双工的,即数据都是在一个方向
上流动的。对然现在某些系统提供全双工管道,但是我们绝不应该假设系统提供这个特性。所以我们最好不要以读写方式打开fifo(另一个原因也是我们不要以读写方式打开,比如我
发送数据到管道让另一个进程接受,然后读管道读取另一个进程给的回复数据,但是我们怎么知道读取的数据是自己发送的还是另一个进程回复的。当然我们可以用另一种IPC机制来协调他们
但是如果这样我们干嘛不用读方式打开一个FIFO然后再用写打开一个FIFO这也避免了编程上的复杂,所以还是最好不用读写方式打开)

言归正传,对于没有用O_NONBLOCK标志的FIFO很容易理解。应为只有FIFO的读写两端都是被打开的情况下open函数才会返回而不阻塞。这里没有什么难点。
关键是对于 使用了 O_NONBLOCK标志打开的FIFO。
如果使用了 O_NONBLOCK 标志。不仅此次的open调用不会阻塞。而且如果open成功返回,后续的I/O操作也不会阻塞。默认情况下一般I/O操作时会阻塞的
什么叫I/O操作不阻塞呢,比如read()函数。我们简单写个测试
    #include
1 #include
  2 #include
  3 #include
  4 
  5 int main(void){
  6         int res;
  7         res=fcntl(0,F_GETFL);
  8         if(res==-1){
  9                 perror("fcntl error");
 10                 exit(1);
 11         }
 12 #ifdef NONBLOCK
 13         res |= O_NONBLOCK;
 14         if(fcntl(0,F_SETFL,res)==-1){
 15                 perror("error");
 16                 exit(1);
 17         }
 18 #endif
 19         char buf[20];
 20         int nread;
 21         nread=read(0,buf,10);
 22         if(nread==-1){
 23                 perror("read error");
 24                 exit(1);
 25         }else{
 26 
 27                 printf("read %d characters\n",nread);
 28         }
 29         exit(0);
 30 }
~        


编译时如果定义 NONBLOCK 那么read 调用就是非阻塞的 我们来看下输出结果


从结果我们可以看出 如果编译时没有定义NONBLOCK  那么 上面代码中就不会编译 #ifndef     #endif 之间的那段代码,标准输入就没有被设置成非阻塞模式,即现在为阻塞模式
我们运行程序,会出现闪动光标让我们输入数据,然后输出读入的数据后输出读入结果程序结束(注意!!回车键也会被当做字符读入,所以输出是读入了4个字符)
然后我们 编译时定义 NONBLOCK  那么  #ifdef   #endif 就也会被一起编译。标准输入就会被设置为非阻塞模式。
我们运行程序,发现read 调用立刻返回。但却是出错返回 -1 相信很多人会以为read调用应该立刻返回0 然后输出 “read 0 characters”。这里正是我们的重点

我们知道 如果 read()读数据时第一个读到的是文件结束标志,那么就返回0。  

那么问题就来了····回到 FIFO 如果写一个尚无以读方式打开的FIFO(比如之前以读方式打开该FIFO的进程关闭了,而现在也没有别的进程以读方式打开FIFO)那么产生信号SIGPIPE.
若某个FIFO的最后一个写进程终止了或关闭了该FIFO,那么将为FIFO的读进程产生一个文件结束标志。

这就是问题的根源。现在 假设一个进程以非阻塞读方式打开一个FIFO.该FIFO之前已经被以写方式被其他进程打开。那么此时读进程需要立刻返回。那么应该返回什么呢。
如果像 我们想的那样 没有数据读应该返回0 ,那么这个0就具有二义性。因为我们不知道是没有数据造成的返回0 还是写端关闭 造成的返回0.
所以POSIX.1要求,对一个非阻塞的描述符如果无数据可读,则read返回-1,而且 errno被设置为 EAGAIN

这就清楚了。
我们总结一下。 对于非阻塞方式打开的 FIFO 他造成后续的i/0 也为非阻塞模式。
1如果以只写方式打开一个之前没有被别的进程以读方式打开的FIFO那么open函数出错返回
2如果以读方式打开一个之前没有别的进程以写方式打开的FIFO。那么成功过返回,
    2.1 如果在随后调用read函数之前,如果另一个进程已经以写方式打开了该FIFO,并写入了数据,那么正常读取数据。
    2.2 如果在随后调用read函数之前,如果另一个进程已经以写方式打开了该FIFO,但是并未写入数据,read调用会立刻出错返回 并设置 errnno为 EAGAIN。
   2.3如果在随后调用read函数之前,没有其他进程以写方式打开该FIFO,或是曾今有但是在read读时已经关闭了,那么read返回0,表示读到文件结束标志。
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值