为什么我不能重用管道与多个子进程通信?
Why can't I reuse a pipe to communicate with multiple child processes?
如果我的父进程有 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, child, 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", i, ppid);
}
}
}
for (int i = 0; i < n; i++){
wait();
}
}
在这里,我尝试重用同一个管道将数据从父级发送到子级,但是在将父级的 pid 发送到第一个子级后管道中断了。我知道解决这个问题的方法是使用 n-1
个管道:每个通信一个。但是,我不明白 为什么 此代码不起作用。任何澄清将不胜感激。谢谢!
(注意:我不是管道和 Unix IPC 专家。)
您可能看到的是管道的预期行为,即通过管道的东西从另一侧出来,而不只是留在那里供其他人观察。一个进程写入,另一个进程读取 - 管道不保留所有数据,并被清除。
也许一个不同的系统调用可以工作,它允许在不清除管道缓冲区的情况下达到峰值,尽管这听起来不像是一个优雅的解决方案。另一种可能的方法是设置一个进程间共享内存区域形式的“海报板”客栈,父进程写入该内存区域,子进程可以读取。
编辑: 您还遇到了在创建所有子项之前过早关闭管道 read-end 的问题。请参阅@MarcoLucidi 的回答。
要阅读的相关文档包括(对于 Linux)syscalls(2), pipe(2), pipe(7), fifo(7), dup2(2), close(2)
当fork(2) fails, you probably want to use errno(3) and perror(3)明白为什么失败了。大多数其他系统调用也可能失败,您需要阅读其文档,并处理它们的失败案例。
你当然需要阅读Advanced Linux Programming
对于非 Linux(例如 FreeBSD)的 Unix 系统,您应该参考它们的文档。
如果您的 C 编译器是 GCC, I recommend reading its documentation then using gcc -Wall -Wextra -g
when compiling, and learn to use the GDB 调试器。
如果您不熟悉 C,请阅读 Modern C book and refer to websites such as this C reference 网站。
研究现有 open source projects coded in C (for example on github.com or gitlab.com) 应该是鼓舞人心的。
您可能对 Frama-C or the Clang static analyzer 等项目感兴趣。
我认为这里的问题是您 close()
父级管道的读取端过早,当其他分叉子级尝试从中读取时,他们得到 EBADF
。你应该总是检查return值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, child, 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]);
}
如果我的父进程有 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, child, 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", i, ppid);
}
}
}
for (int i = 0; i < n; i++){
wait();
}
}
在这里,我尝试重用同一个管道将数据从父级发送到子级,但是在将父级的 pid 发送到第一个子级后管道中断了。我知道解决这个问题的方法是使用 n-1
个管道:每个通信一个。但是,我不明白 为什么 此代码不起作用。任何澄清将不胜感激。谢谢!
(注意:我不是管道和 Unix IPC 专家。)
您可能看到的是管道的预期行为,即通过管道的东西从另一侧出来,而不只是留在那里供其他人观察。一个进程写入,另一个进程读取 - 管道不保留所有数据,并被清除。
也许一个不同的系统调用可以工作,它允许在不清除管道缓冲区的情况下达到峰值,尽管这听起来不像是一个优雅的解决方案。另一种可能的方法是设置一个进程间共享内存区域形式的“海报板”客栈,父进程写入该内存区域,子进程可以读取。
编辑: 您还遇到了在创建所有子项之前过早关闭管道 read-end 的问题。请参阅@MarcoLucidi 的回答。
要阅读的相关文档包括(对于 Linux)syscalls(2), pipe(2), pipe(7), fifo(7), dup2(2), close(2)
当fork(2) fails, you probably want to use errno(3) and perror(3)明白为什么失败了。大多数其他系统调用也可能失败,您需要阅读其文档,并处理它们的失败案例。
你当然需要阅读Advanced Linux Programming
对于非 Linux(例如 FreeBSD)的 Unix 系统,您应该参考它们的文档。
如果您的 C 编译器是 GCC, I recommend reading its documentation then using gcc -Wall -Wextra -g
when compiling, and learn to use the GDB 调试器。
如果您不熟悉 C,请阅读 Modern C book and refer to websites such as this C reference 网站。
研究现有 open source projects coded in C (for example on github.com or gitlab.com) 应该是鼓舞人心的。
您可能对 Frama-C or the Clang static analyzer 等项目感兴趣。
我认为这里的问题是您 close()
父级管道的读取端过早,当其他分叉子级尝试从中读取时,他们得到 EBADF
。你应该总是检查return值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, child, 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]);
}