(转)关于pipe()的详细解析
2008-06-10 09:17 htc开发 阅读(288) 评论(0) 编辑 收藏 举报 kevintz 2000.8.23
int pipe(int fd[2])函数在内核生成一个管道,如图。返回的
fd[0]描述符用于从管道读内容,fd[1]用于向管道写。
---------------------
fd[0]<--| |<---fd[1]
---------------------
读的时候,如果管道没数据,读进程阻塞。如果写的时候管道满,
写进程阻塞。可以把fd[0], fd[1]设成是非阻塞,在上面的阻塞情形,
不再阻塞进程,立刻返回。
管道的某一端只能是只读或只写,如果向fd[0]写和向fd[1]读都会
出错,返回-1。
另外,fork()调用生成的子进程和原来的父进程是共用这条管道的,
并不是又生成另一条管道。如图所示:
+------------------------+
| |
------------- +- ----------- <-----+ | -----------
父 | fd[0] |<----- | | <---+ | +->| fd[0] | 子
| | ----------- | | | |
| fd[1] |------------------------+ +--- | fd[1] |
| ........ | | ..... |
而且对于子进程来说,fd[0]也是用于只读,fd[1]也是用于写,因为
它们的指向和父进程里是一样的。
下面来分析一下网友startrek写的程序,并分析为何出错。
int main(){
int fd[2];
char buf[200],len;
int status;
if(pipe(fd)==0){
if(fork()==0){
len=read(fd[0],buf,sizeof(buf));
buf[len]=0;
printf("[Child]:%s/n",buf);
sprintf(buf,"answer from child");
write(fd[1],buf,strlen(buf));
}
else{
sprintf(buf,"string from parent");
write(fd[1],buf,strlen(buf));
sleep(2);
len=read(fd[0],buf,sizeof(buf)); // ***
buf[len]=0;
printf("[Parent]:%s/n",buf);
}
}
wait(&status);
}
[情况1]
如果注释掉sleep(2);这一句,父进程就会接收自己传给子进程的
字符串,显示
[Parent]:string from parent
而子进程就因为无数据读而阻塞。这说明父进程和子进程之间只有
一条管道队列进行双向传输,父进程读取数据后管道为空,所以子
进程阻塞。
kevintz分析:其实这个结果是正确的。原因如下:去掉sleep(2)后,当父进
程写fd[1]后,继续运行read(..),所以父进程把自己写进去的东西读
了出来。而到子进程运行时,就因为没数据读而阻塞了。注意:管道
只能是单向传输的!
[情况2]
但如果将标有***的一句改为
len=read(fd[1],buf,sizeof(buf)),而sleep(2);不注释,就会有
下面奇怪的显示结果:
[Child]:string from parent
[Parent]:string from parent
按道理说在子进程退出后,管道中应该只有"answer from child"。
kevintz分析:这个结果也是正确的。其实子进程是已经读到了父进程的内容,
而且已经写回了管道里。不过因为不论是父进程还是子进程对fd[1]读,
都是出错的,返回-1,而buf里的东西还是sprintf进去的东西,没变。
反而buf[len]就是buf[-1]=0这句有潜在的危险。然后你把buf打印,所
以还是原来的内容。管道的某一端都只能是只读或只写的!
[情况3]
和情况2相同,在最后加3行,即父进程使用fd[0]进行read,其
显示结果为
[Child]:string from parent
[Parent]:string from parent
[Parent]:answer from child
kevintz分析:这个例子就正好做了情况2的解析。说明子进程的确收到了父进程
的信息,而且写回了管道,而父进程应该用read(fd[0]...)来读的,所
以读到了子进程写回的信息。
[情况4]
如果一开始父进程多传一个串"string 2 from parent",其结果
就更复杂。
kevintz分析:呵呵:),多传一个串,结果慢慢去分析吧................
总结:
其实这种通信,应该用两条管道来形成一个双向的交流管道,而不应该用一
条管道。
当然你可以用信号量来同步管道的使用,但太复杂,而且不实际。双向管道
可以这样实现。
int main()
{
int fd1[2],fd2[2],len;
char buf[128];
int status;
pipe(fd1);
pipe(fd2);
if( fork() == 0)
{
close(fd1[0]);
close(fd2[1]);
len=read(fd2[0], buf, 128);
buf[len]=0;
printf("[Child]:%s/n",buf);
sprintf(buf,"answer from child");
write(fd1[1], buf, strlen(buf));
close(fd2[0]);
close(fd1[1]);
}
else
{
close(fd1[1]);
close(fd2[0]);
sprintf(buf, "Hello from parent");
write(fd2[1], buf, strlen(buf));
len=read(fd1[0],buf,sizeof(buf));
buf[len]=0;
printf("[Parent]:%s/n",buf);
close(fd1[0]);
close(fd2[1]);
}
wait(&status);
exit(0);
}
--
※ 修改:.kevintz 于 Aug 23 11:08:13 修改本文.[FROM: 61.140.71.100]