读取一个 Child 进程阻塞其他 Children
Reading From One Child Process Blocks Other Children
正如您从我在下面分享的代码中看到的那样,我正在尝试实施 1 个中央服务器和 n
children。首先,我通过从服务器分叉来创建所有 n
children。此外,在分叉之前,我使用 Pipe
创建双向管道。分叉并创建 n
children 后,我想从 child 代码中读取字符。他们都发送字符 'A',但我只能从一个 child 读取。我使用 poll to select active children 并尝试从中读取数据。什么可能导致程序在一个 child 上阻塞?我将不胜感激任何帮助。
#define Pipe(fd) socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, fd)
int n = 5;
struct pollfd pfd[n];
for (int i = 0; i < n; i++){
Pipe(fd[i]);
if (fork() == 0){
close(fd[i][0]);
dup2(fd[i][1], 0);
dup2(fd[i][1], 1);
close(fd[i][1]);
execl(code, arguments);
}
else {
close(fd[i][1]);
dup2(fd[i][0], 0);
dup2(fd[i][0], 1);
close(fd[i][0]);
}
struct pollfd s;
s.fd = fd[i][1];
s.events = POLLIN;
s.revents = 0;
pfd[i] = s;
}
while(1){
poll(pfd, n, 2);
for (int i = 0; i < n; i++){
if (pfd[i].revents && POLLIN){
char data;
read(0, &data, 1);
if (data == 'A'){
.
.
.
}
}
}
}
父级只能有一个标准输入文件描述符和一个标准输出文件描述符。你的代码是这样的:
for (int i = 0; i < n; i++){
Pipe(fd[i]);
if (fork() == 0){
close(fd[i][0]);
dup2(fd[i][1], 0);
dup2(fd[i][1], 1);
close(fd[i][1]);
execv(code, arguments);
}
else {
close(fd[i][1]);
dup2(fd[i][0], 0);
dup2(fd[i][0], 1);
close(fd[i][0]);
}
…poll setup…
}
这会在每次创建子项时仔细切换父项中的标准输入和标准输出,使程序只与最后一个子项建立连接。
您不应该像在父级中那样使用 dup2()
;您可能需要在数组中保持文件描述符处于打开状态,以便使用它们分别与子项通信。
我没有检查过你为什么在 fd[i]
之后使用下标 [0]
和 [1]
,但感觉不对。
您应该在 execv()
调用之后至少放置一个 exit(1);
或 _exit(1);
;我更希望看到一条错误消息也打印到 stderr
,报告 execv()
失败。您不需要检查 return 值;如果 execv()
returns,失败;如果成功,则不会 return。否则,如果 execv()
失败,您可能会得到多个进程都认为它们是父进程。这会导致混乱。始终确保您处理执行其他程序的失败!
正如您从我在下面分享的代码中看到的那样,我正在尝试实施 1 个中央服务器和 n
children。首先,我通过从服务器分叉来创建所有 n
children。此外,在分叉之前,我使用 Pipe
创建双向管道。分叉并创建 n
children 后,我想从 child 代码中读取字符。他们都发送字符 'A',但我只能从一个 child 读取。我使用 poll to select active children 并尝试从中读取数据。什么可能导致程序在一个 child 上阻塞?我将不胜感激任何帮助。
#define Pipe(fd) socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, fd)
int n = 5;
struct pollfd pfd[n];
for (int i = 0; i < n; i++){
Pipe(fd[i]);
if (fork() == 0){
close(fd[i][0]);
dup2(fd[i][1], 0);
dup2(fd[i][1], 1);
close(fd[i][1]);
execl(code, arguments);
}
else {
close(fd[i][1]);
dup2(fd[i][0], 0);
dup2(fd[i][0], 1);
close(fd[i][0]);
}
struct pollfd s;
s.fd = fd[i][1];
s.events = POLLIN;
s.revents = 0;
pfd[i] = s;
}
while(1){
poll(pfd, n, 2);
for (int i = 0; i < n; i++){
if (pfd[i].revents && POLLIN){
char data;
read(0, &data, 1);
if (data == 'A'){
.
.
.
}
}
}
}
父级只能有一个标准输入文件描述符和一个标准输出文件描述符。你的代码是这样的:
for (int i = 0; i < n; i++){
Pipe(fd[i]);
if (fork() == 0){
close(fd[i][0]);
dup2(fd[i][1], 0);
dup2(fd[i][1], 1);
close(fd[i][1]);
execv(code, arguments);
}
else {
close(fd[i][1]);
dup2(fd[i][0], 0);
dup2(fd[i][0], 1);
close(fd[i][0]);
}
…poll setup…
}
这会在每次创建子项时仔细切换父项中的标准输入和标准输出,使程序只与最后一个子项建立连接。
您不应该像在父级中那样使用 dup2()
;您可能需要在数组中保持文件描述符处于打开状态,以便使用它们分别与子项通信。
我没有检查过你为什么在 fd[i]
之后使用下标 [0]
和 [1]
,但感觉不对。
您应该在 execv()
调用之后至少放置一个 exit(1);
或 _exit(1);
;我更希望看到一条错误消息也打印到 stderr
,报告 execv()
失败。您不需要检查 return 值;如果 execv()
returns,失败;如果成功,则不会 return。否则,如果 execv()
失败,您可能会得到多个进程都认为它们是父进程。这会导致混乱。始终确保您处理执行其他程序的失败!