如何解决为什么不能重用管道与多个子进程进行通信?
如果我的父进程具有n
个子进程,为什么我不能使用一个管道将父进程的pid发送给所有子进程? post对此进行了简要介绍,
但我觉得解释得还不够清楚。
这是我的代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void main(void){
int n;
printf("How many child processes would you like to create?");
scanf("%d",&n);
pid_t pid,child,ppid,pidout;
int fd[2];
pipe(fd);
for (int i = 0; i < n; i++){
pid = fork();
switch(pid){
case -1:
printf("error forking at index %i",i);
exit(1);
case 0:
close(fd[1]);
child = getpid();
read(fd[0],&pidout,4);
printf("\nNode : %i\nMy pid is : %i\nMy parent's pid is : %i\n\n",i,pidout);
exit(0);
default:
close(fd[0]);
ppid = getpid();
write(fd[1],&ppid,4);
if (!i){
printf("\nNode : %i,My pid is : %i\n\n",ppid);
}
}
}
for (int i = 0; i < n; i++){
wait();
}
}
在这里,我尝试重用同一管道以将数据从父级发送到子级,但是在将父级的pid发送给第一个子级之后,该管道断开了。我了解解决此问题的方法是使用n-1
个管道:每个通信一个管道。但是,我不明白为什么为什么该代码不起作用。任何澄清将不胜感激。谢谢!
解决方法
(注意:我不是管道和Unix IPC专家。)
您可能看到的是管道的预期行为,即管道中经过的东西从另一侧出来,而不仅仅是留在那儿供其他人观察。一个进程写入,另一个进程读取-管道没有保留所有数据,而是将其清除。
也许一个不同的系统调用可以起作用,该调用允许在管道的缓冲区达到峰值而无需清除它,尽管这听起来不算是一种优雅的解决方案。另一种可能的方法是以进程间共享存储区的形式设置“海报板”,父进程将写入该区域,子进程可以读取。
编辑:在创建所有子项之前,您还存在管道的读取端过早关闭的问题。参见@MarcoLucidi的答案。
,我认为这里的问题是,您close()
早于父级中的管道读取端,并且当其他分叉的子级尝试从中读取时,它们会得到EBADF
。您应该始终检查read()
和write()
的返回值。
如果您在close(fd[0]);
调用后最后移动wait()
,则代码应该可以正常工作。
这里是代码的有效重写,以说明我的意思:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int n;
printf("How many child processes would you like to create?");
if (scanf("%d",&n) != 1) {
fprintf(stderr,"invalid number\n");
exit(EXIT_FAILURE);
}
pid_t pid,child,ppid,pidout;
int fd[2];
if (pipe(fd) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
for (int i = 0; i < n; i++) {
pid = fork();
switch (pid) {
case -1:
printf("error forking at index %i",i);
exit(EXIT_FAILURE);
case 0: /* child */
close(fd[1]);
child = getpid();
if (read(fd[0],&pidout,sizeof(pidout)) < 0) {
perror("read");
exit(EXIT_FAILURE);
}
printf("Node: %i My pid is: %i My parent's pid is: %i\n",i,pidout);
close(fd[0]);
exit(EXIT_SUCCESS);
default: /* parent */
ppid = getpid();
if (write(fd[1],&ppid,sizeof(ppid)) < 0) {
perror("write");
exit(EXIT_FAILURE);
}
if (i == 0)
printf("\nParent,My pid is: %i\n\n",ppid);
}
}
for (int i = 0; i < n; i++)
wait(NULL);
/* close pipe in the parent at the end,to avoid bad file descriptors
* in the forked children */
close(fd[0]);
close(fd[1]);
}
,
要阅读的相关文档包括(对于Linux)syscalls(2),pipe(2),pipe(7),fifo(7),dup2(2),close(2) >
当fork(2)失败时,您可能想使用errno(3)和perror(3)来了解失败的原因。而且大多数其他系统调用也可能失败,因此您需要阅读其文档并处理其失败案例。
您当然需要阅读Advanced Linux Programming
对于不是Linux的Unix系统(例如FreeBSD),您应该参考其文档。
如果您的C编译器是GCC,建议您阅读其文档,然后在编译时使用gcc -Wall -Wextra -g
,并学习使用GDB调试器。
如果您不熟悉C,请阅读Modern C书,并访问this C reference等网站。
研究以C语言编码的现有 open source项目(例如在github.com或gitlab.com上)应该是鼓舞人心的。
您可能对Frama-C或Clang static analyzer等项目感兴趣。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。