Linux命名管道特性总结
话不多说,直接总结命名管道的特性吧!
1.必须同时有读端和写端。假如只有一边,那么open()函数将会阻塞。
2.打开管道时,必须有读的一端和写的一端,否则打开的open()会阻塞。
3.在写入时,如果没有读端,写入方将会受到SIGPIPE信号。
4.默认情况下,读取是阻塞的,写入是非阻塞的。
下面用代码实测:
#include <head.h>
int kill(pid_t,int);
void sigHandler2(int no)
{
printf("[%d] receive signal %d\n",getpid(),no);
}
int main(int argc ,char **argv)
{
if(argc != 2){
printf("Usage :%s fifo name\n",argv[0]);
exit(1);
}
int i=0;
for ( ; i < 1; i++)
{
pid_t pid =fork();
if(pid == 0){
break;
}
}
if(i == 1){
int fifo = open (argv[1],O_RDWR);
if(fifo == -1){
perror(" open error ");
exit(1);
}
printf("[%d],fifo fd =%d\n",getpid(),fifo);
char buf[1024];
int len = 0;
while (1)
{
memset(buf,0,sizeof(buf));
len = read(fifo,buf,1024);
printf("father:afte open\n");
if(len <0){
perror(" read error");
exit(1);
}
printf("%c\n",buf[0]);
#if 0
close(fifo);
while(1);
#endif
}
}
else{
signal(SIGPIPE,sigHandler2);
printf("before open\n");
int fifo = open (argv[1],O_WRONLY);
printf("son:afte open\n");
if(fifo == -1){
perror(" open error ");
exit(1);
}
printf("[%d],fifo fd =%d\n",getpid(),fifo);
int x='A';
for ( ;;)
{
write(fifo,&x,4);
++x;
sleep(1);
}
}
return 0;
}
首先测试1,注释掉父进程的打开fifo的代码,原来是 int fifo = open (argv[1],O_RDWR);
改成int fifo =0;
然后运行,得到的结果是
user@ubuntu18:~/Music$ ./named_fifo fifo1
[55849],fifo fd =0
son before open
之后就是光标一直闪烁,无任何输出了,没有后面的"son:afte open\n",这就说明,只有一端打开管道,会导致打开阻塞。
测试性质2
把父进程的打开时的参数改成O_WRONLY,也就是两边都已只写的方式打开,这也会导致open阻塞。
测试结果同上。
测试3
把父进程里买呢的#if 0 改为#if 1 ,也是说,父进程读1次之后就把管道关闭了。测试结果如下:
user@ubuntu18:~/Music$ ./named_fifo fifo1
[60454],fifo fd =3
son before open
son:afte open
[60455],fifo fd =3
father:afte open
A
[60455] receive signal 13
[60455] receive signal 13
[60455] receive signal 13
[60455] receive signal 13
[60455] receive signal 13
从测试结果可以看出,子进程在父进程关闭读端之后,再次向管道写入数据,会收到SIGPIPE的信号,man page 对于SIGPIPE的说明如下:
SIGPIPE 13 Term Broken pipe: write to pipe with no readers;
简单翻译就是在没有读端的情况下向管道写数据将会收到此信号。
测试4
修改父子进程的逻辑如下
if(i == 1){
int fifo =0;
fifo = open (argv[1],O_RDWR);
if(fifo == -1){
perror(" open error ");
exit(1);
}
printf("[%d],fifo fd =%d\n",getpid(),fifo);
char buf[1024];
int len = 0;
while(1);
#if 0
while (1)
{
memset(buf,0,sizeof(buf));
len = read(fifo,buf,1024);
printf("father:afte open\n");
if(len <0){
perror(" read error");
exit(1);
}
printf("%c\n",buf[0]);
#endif
//}
}
else{
signal(SIGPIPE,sigHandler2);
printf("son before open\n");
int fifo = open (argv[1],O_WRONLY);
printf("son:afte open\n");
if(fifo == -1){
perror(" open error ");
exit(1);
}
printf("[%d],fifo fd =%d\n",getpid(),fifo);
int x='A';
for ( ;;)
{
int len= 0;
printf("son before write\n");
len = write(fifo,&x,4);
printf("son after write,len =%d\n",len);
++x;
sleep(1);
}
}
得到如下的测试结果:
user@ubuntu18:~/Music$ ./named_fifo fifo1
[63688],fifo fd =3
son before open
son:afte open
[63689],fifo fd =3
son before write
son after write,len =4
son before write
son after write,len =4
son before write
son after write,len =4
son before write
父进程里面是打开管道后什么也不做,子进程一直在向管道里面写数据,写的过程没有阻塞。
可以看出,在管道写满之前,write函数不会被阻塞。
修改关键代码如下:
if(i == 1){
int fifo =0;
fifo = open (argv[1],O_RDWR);
if(fifo == -1){
perror(" open error ");
exit(1);
}
char buf[1024];
int len = 0;
#if 1
while (1)
{
memset(buf,0,sizeof(buf));
printf("father before read\n");
len = read(fifo,buf,1024);
printf("father:afte read\n");
if(len <0){
perror(" read error");
exit(1);
}
printf("%c\n",buf[0]);
#endif
}
}
else{
signal(SIGPIPE,sigHandler2);
int fifo = open (argv[1],O_WRONLY);
if(fifo == -1){
perror(" open error ");
exit(1);
}
int x='A';
for (int k=0 ;k<1;k++)
{
int len= 0;
len = write(fifo,&x,4);
++x;
sleep(1);
}
while(1);
}
上述的改动就是让子进程只向管道写一次数据。父进程一直从管道读取数据。
得到以下的测试结果:
user@ubuntu18:~/Music$ ./named_fifo fifo1
father before read
father:afte read
A
father before read
可以看出,在管道没有数据时,从管道的读操作将会引起阻塞。