對於不使用的管道讀寫段需要關閉的思考

爲什麼需要關閉不使用的讀端和寫端?不關閉行不行?

我們看下面一段代碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章