new_learner
关于管道写阻塞的问题
无名管道,我们知道,进程在管道上调用read时,如果管道非空,read就立即返回。如果管道为空,那么只要有进程为写操作打开管道,read就会一直阻塞到某些内容被写入管道为止。另一方面,如果没有进程为写操作打开管道,对空管道进行的read调用就返回0,标识遇到了文件结束(这里所描述的情况假定对管道的访问使用的是阻塞型I/O)。
所以在父子进程之间想通过管道实现重定向的时候,在dup2之后,都会把管道描述符fd[0]和fd[1] close掉,防止read的时候阻塞,如下面的代码,实现shell中" ls -l | sort -n +4"这样的功能:
[table=95%][tr][td][font=FixedSys][color=#000000][color=#0000CC]#[/color][color=#FF0000]include[/color] [color=#0000CC]<[/color][color=#FF0000]errno[/color][color=#0000CC].[/color]h[color=#0000CC]>[/color]
[color=#0000CC]#[/color][color=#FF0000]include[/color] [color=#0000CC]<[/color]stdio[color=#0000CC].[/color]h[color=#0000CC]>[/color]
[color=#0000CC]#[/color][color=#FF0000]include[/color] [color=#0000CC]<[/color]unistd[color=#0000CC].[/color]h[color=#0000CC]>[/color]
[color=#0000CC]#[/color][color=#FF0000]include[/color] [color=#0000CC]<[/color]sys[color=#0000CC]/[/color]types[color=#0000CC].[/color]h[color=#0000CC]>[/color]
[color=#0000FF]int[/color] main[color=#0000CC]([/color][color=#0000FF]void[/color][color=#0000CC])[/color] [color=#0000CC]{[/color]
[color=#FF0000]pid_t[/color] childpid[color=#0000CC];[/color]
[color=#0000FF]int[/color] fd[color=#0000CC][[/color]2[color=#0000CC]][/color][color=#0000CC];[/color]
[color=#0000FF]if[/color] [color=#0000CC]([/color][color=#0000CC]([/color]pipe[color=#0000CC]([/color]fd[color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color] [color=#0000CC]|[/color][color=#0000CC]|[/color] [color=#0000CC]([/color][color=#0000CC]([/color]childpid [color=#0000CC]=[/color] fork[color=#0000CC]([/color][color=#0000CC])[/color][color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color][color=#0000CC])[/color] [color=#0000CC]{[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to setup pipeline"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000FF]return[/color] 1[color=#0000CC];[/color]
[color=#0000CC]}[/color]
[color=#0000FF]if[/color] [color=#0000CC]([/color]childpid [color=#0000CC]=[/color][color=#0000CC]=[/color] 0[color=#0000CC])[/color] [color=#0000CC]{[/color] [color=#FF9900]/* ls is the child */[/color]
[color=#0000FF]if[/color] [color=#0000CC]([/color]dup2[color=#0000CC]([/color]fd[color=#0000CC][[/color]1[color=#0000CC]][/color][color=#0000CC],[/color] STDOUT_FILENO[color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to redirect stdout of ls"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000FF]else[/color] [color=#0000FF]if[/color] [color=#0000CC]([/color][color=#0000CC]([/color][color=#FF0000]close[/color][color=#0000CC]([/color]fd[color=#0000CC][[/color]0[color=#0000CC]][/color][color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color] [color=#0000CC]|[/color][color=#0000CC]|[/color] [color=#0000CC]([/color][color=#FF0000]close[/color][color=#0000CC]([/color]fd[color=#0000CC][[/color]1[color=#0000CC]][/color][color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color][color=#0000CC])[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to close extra pipe descriptors on ls"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000FF]else[/color] [color=#0000CC]{[/color]
execl[color=#0000CC]([/color][color=#FF00FF]"/bin/ls"[/color][color=#0000CC],[/color] [color=#FF00FF]"ls"[/color][color=#0000CC],[/color] [color=#FF00FF]"-l"[/color][color=#0000CC],[/color] [color=#FF0000]NULL[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to exec ls"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000CC]}[/color]
[color=#0000FF]return[/color] 1[color=#0000CC];[/color]
[color=#0000CC]}[/color]
[color=#0000FF]if[/color] [color=#0000CC]([/color]dup2[color=#0000CC]([/color]fd[color=#0000CC][[/color]0[color=#0000CC]][/color][color=#0000CC],[/color] STDIN_FILENO[color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color] [color=#FF9900]/* sort is the parent */[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to redirect stdin of sort"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000FF]else[/color] [color=#0000FF]if[/color] [color=#0000CC]([/color][color=#0000CC]([/color][color=#FF0000]close[/color][color=#0000CC]([/color]fd[color=#0000CC][[/color]0[color=#0000CC]][/color][color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color] [color=#0000CC]|[/color][color=#0000CC]|[/color] [color=#0000CC]([/color][color=#FF0000]close[/color][color=#0000CC]([/color]fd[color=#0000CC][[/color]1[color=#0000CC]][/color][color=#0000CC])[/color] [color=#0000CC]=[/color][color=#0000CC]=[/color] [color=#0000CC]-[/color]1[color=#0000CC])[/color][color=#0000CC])[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to close extra pipe file descriptors on sort"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000FF]else[/color] [color=#0000CC]{[/color]
execl[color=#0000CC]([/color][color=#FF00FF]"/bin/sort"[/color][color=#0000CC],[/color] [color=#FF00FF]"sort"[/color][color=#0000CC],[/color] [color=#FF00FF]"-n"[/color][color=#0000CC],[/color] [color=#FF00FF]"+4"[/color][color=#0000CC],[/color] [color=#FF0000]NULL[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#FF0000]perror[/color][color=#0000CC]([/color][color=#FF00FF]"Failed to exec sort"[/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000CC]}[/color]
[color=#0000FF]return[/color] 1[color=#0000CC];[/color]
[color=#0000CC]}[/color]
[/color][/font][/td][/tr][/table]
其中,父进程的处理代码中,close(fd[0])和close(fd[1])就是为了防止父进程的sort函数从标准输入中读阻塞。
那么,当一个进程write 管道时,在什么情况下会阻塞呢?
上述代码,我把子进程的close(fd[0])和close(fd[1])都注释掉,程序照样能够正常工作?那为什么子进程的代码需要close这两个管道的fd呢?或者说,这根本就是多余的?
谢谢!
xi2008wang
不需要的fd就尽量关闭,
如果你没关闭fd[0],fd[1],那么exec后的进程也会有这两个fd
new_learner
那write pipe什么时候会阻塞呢?
ps:LS的头像难道就是我年轻的时候?:mrgreen: :mrgreen: 看来我老了。。。
xi2008wang
当管道没有空间时,write是会阻塞
而且管道最小是4k
new_learner
多谢!~
我想实验一下write阻塞的情况,那我怎么保证write的管道没有空间内?
xi2008wang
[code]
else {
sleep(20); //加上这一行,让父进程睡眠20s
execl("/bin/sort", "sort", "-n", "+4", NULL);
perror("Failed to exec sort");
}
[root@mylinux ~]# ./a.out &
[1] 25578
[root@mylinux ~]# ps
PID TTY TIME CMD
13154 pts/0 00:00:00 bash
25578 pts/0 00:00:00 a.out //父进程
25579 pts/0 00:00:00 ls <defunct> //子进程已被阻塞,不然它都已经执行结束了
25723 pts/0 00:00:00 ps
[root@mylinux ~]#
[/code]
new_learner
这里ls为什么会阻塞呢?
pipe里面并没有东西,write操作为什么会阻塞呢?
不是说管道最小是4k吗?
还是不太明白。。。:em16: :em16:
xi2008wang
:oops:
我也是初学者,看来我理解错了
[url]http://blog.csdn.net/DL88250/archive/2007/06/25/1665353.aspx[/url]
new_learner
多谢提供链接。
原来你给出的代码write阻塞是由于这个原因:
“向管道中写入数据:
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
”
但是如果利用fifo进行通信,则没有这个限制。