对于不使用的管道读写段需要关闭的思考

为什么需要关闭不使用的读端和写端?不关闭行不行?

我们看下面一段代码

	#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。

发布了31 篇原创文章 · 获赞 4 · 访问量 960

猜你喜欢

转载自blog.csdn.net/qq_39781096/article/details/104078742