为什么需要关闭不使用的读端和写端?不关闭行不行?
我们看下面一段代码
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
pid_t pid;
int fp[2];
int ret=pipe(fp);
int i;
char *s[5];
char *t[5];
s[0]="ls";
s[1]="-l";
s[2]=NULL;
t[0]="wc";
t[1]=NULL;
for(i=0;i<2;i++)
{
pid=fork();
if(pid==-1)
{
perror("pid error:");
exit(1);
}
if(pid == 0){
break;
}
}
if(i == 0)
{
close(fp[0]);
dup2(fp[1],STDOUT_FILENO);
execvp(s[0],s);
}
else if(i == 1){
close(fp[1]);
dup2(fp[0],STDIN_FILENO);
execvp(t[0],t);
}
int set;
if(pid > 0){
do{
set = waitpid(-1,NULL,WNOHANG);
}while(set == 0);
}
return 0;
}
运行结果为
输出正常,但如果对整个main里的代码加上while循环的话结果就不一样了
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
while(1)
{
pid_t pid;
int fp[2];
int ret=pipe(fp);
int i;
char *s[5];
char *t[5];
s[0]="ls";
s[1]="-l";
s[2]=NULL;
t[0]="wc";
t[1]=NULL;
for(i=0;i<2;i++)
{
pid=fork();
if(pid==-1)
{
perror("pid error:");
exit(1);
}
if(pid == 0){
break;
}
}
if(i == 0)
{
close(fp[0]);
dup2(fp[1],STDOUT_FILENO);
execvp(s[0],s);
}
else if(i == 1){
close(fp[1]);
dup2(fp[0],STDIN_FILENO);
execvp(t[0],t);
}
int set;
if(pid > 0){
do{
set = waitpid(-1,NULL,WNOHANG);
}while(set == 0);
}
}
return 0;
}
没有任何结果输出
通过 ps aux 查看进程会发现有很多的阻塞的wc进程
为何会出现这种情况?
这里就是涉及到标题了,为何要把不使用的读写端关闭,上面的程序没有把父进程对pipe读写端关闭,使得读写端的计数在最小的时候都>0。
当pipe写端引用计数>0时,读端读完管道里的数据后会阻塞等待写端写入。
当pipe写端引用计数为0时,读端读完数据直接返回0,进程结束。
所以当父进程没有关闭读写端的时候,wc进程读完管道里的内容后会阻塞等待父进程写端写入,而我们程序的父进程明显没有写的操作,所有wc就会一直阻塞等待了。
但是为何没有while循环就会有正常的结果输出?
当一个进程结束后,会释放掉它所占有的文件描述符和用户空间,只保留pcb,里面保存着导致它结束的信号,变成僵尸进程等待父进程收尸。所以没有while循环的话当父进程结束后,pipe写端的引用计数就变为0,wc进程就不会阻塞等待了,所以就会有正常结果输出。
所以,我们加入父进程对于pipe读端写端的close操作就好了
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
while(1)
{
pid_t pid;
int fp[2];
int ret=pipe(fp);
int i;
char *s[5];
char *t[5];
s[0]="ls";
s[1]="-l";
s[2]=NULL;
t[0]="wc";
t[1]=NULL;
for(i=0;i<2;i++)
{
pid=fork();
if(pid==-1)
{
perror("pid error:");
exit(1);
}
if(pid == 0){
break;
}
}
if(i == 0)
{
close(fp[0]);
dup2(fp[1],STDOUT_FILENO);
execvp(s[0],s);
}
else if(i == 1){
close(fp[1]);
dup2(fp[0],STDIN_FILENO);
execvp(t[0],t);
}
int set;
if(pid > 0){
do{
close(fp[0]);
close(fp[1]);
set = waitpid(-1,NULL,WNOHANG);
}while(set == 0);
}
}
return 0;
}
可以看到输出正确
综上,在我们使用管道进行进程间通信时,一定要对不使用的读写端进行close操作,否则会因为读写端引用计数>0,出现一些BUG。