爲什麼需要關閉不使用的讀端和寫端?不關閉行不行?
我們看下面一段代碼
#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。