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,表示读到文件结束标志。
阅读(9933) | 评论(0) | 转发(0) |