我似乎对您的程序的行为有一个合理的解释。正如我已经在 cmets 中指出的那样,程序中存在一个明显的缺陷,即未设置 int pipes[N][2];,这最终会出现在 close() API 中,实际上只是导致 EBADF 被返回并且可能没有达到预期的效果。
至于程序的行为,可以通过检查数组int pipes[N][2]; 中填充的实际值来解释。由于原始问题中没有明确设置这些值,因此这些值基本上是未定义的,但是......
让我们尝试使用以下方法将它们设置为更合理的值:
int pipeVal = INT32_MIN;
if(argc == 2) {
pipeVal = atoi(argv[1]);
}
for(int i = 0; i < N; ++i) {
if(pipeVal != INT32_MIN) {
pipes[i][0] = pipes[i][1] = pipeVal;
}
printf("%d: Pipe pair %d-%d\n",getpid(), pipes[i][0], pipes[i][1]);
}
传入 0 产生输出:
61962: Number of process is : 3
61962: Pipe pair 0-0
61962: Pipe pair 0-0
61962: Pipe pair 0-0
61963: Election Id 0, numProc 0
61963: Exiting while-loop
61964: Election Id 0, numProc 1
61964: Exiting while-loop
61965: Election Id 0, numProc 2
61965: Exiting while-loop
这似乎与预期一致:fork 3 个进程最终会退出。让我们尝试使用 1 的值
62033: Number of process is : 3
62033: Pipe pair 1-1
62033: Pipe pair 1-1
62033: Pipe pair 1-1
62034: Election Id 0, numProc 0
62035: Election Id 2, numProc 1
62036: Election Id 0, numProc 2
现在这似乎很奇怪,因为没有进程真正退出while循环,但主程序成功终止。为了实际检查子程序的退出状态,我添加了以下代码:
if (getpid()==pere)
{
/*On attend la fin de chaque processus
pour mettre un terme au processu pere*/
for (int i = 0; i < N; ++i)
{
errno = 0;
int stat_val;
wait(&stat_val);
#if DBG == 1
printf("%d: WIFEXITED(%d),WEXITSTATUS(%d),WIFSIGNALED(%d),WTERMSIG(%d),WIFSTOPPED(%d),WSTOPSIG(%d),WIFCONTINUED(%d)\n", getpid(), WIFEXITED(stat_val),WEXITSTATUS(stat_val),WIFSIGNALED(stat_val),WTERMSIG(stat_val),WIFSTOPPED(stat_val),WSTOPSIG(stat_val),WIFCONTINUED(stat_val));
#endif
}
}
这是传入 1 作为参数的输出:
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
62215: WIFEXITED(1),WEXITSTATUS(0),WIFSIGNALED(0),WTERMSIG(0),WIFSTOPPED(0),WSTOPSIG(0),WIFCONTINUED(0)
这个输出表明所有进程正常退出,没有被任何信号异常停止。
线索在这些进程试图关闭的实际fds 中。在 Linux 中(至少)一个进程一旦创建就有 3 个文件描述符:0 对应于stdin,1 对应于stdout,2 对应于stderr。当 0 作为参数传递给程序时,进程最终会关闭 stdin (close(0)) 无论如何都没有使用,因此没有问题,它们随后退出 comparerIdElection() 和 while 循环。当 1 作为参数传递给程序时,进程最终会关闭 stdout (close(1)),因此应该在控制台上看不到任何应该写入输出的内容,但它们仍然 退出comparerIdElection() 和随后的while 循环,从而退出每个进程的WIFEXITED(1)。
现在来看实际共享的程序,未指定最终填充的实际值是什么,但如果数组中的 N*2 fds 之一设置为 1,则行为如上 (传入 1) 将被展示,即即使printfs 不会被写入控制台,进程确实会退出 while 循环,并且主父进程能够成功地等待进程。在我的电脑上(我也怀疑在你的电脑上),这些数组元素中的至少一个的实际值始终设置为 1。
我的 Mac 上管道 fd 对的值:
61120: Pipe pair -296602944-32766
61120: Pipe pair 23978080-1
61120: Pipe pair 64-0
他们在TryItOnline上的价值观:
25090: Pipe pair 0-0
25090: Pipe pair 68079952-32708
25090: Pipe pair 1-0
它们在OnlineGDB上的值(需要添加编译器标志-DNBPROC=3):
5612: Pipe pair 1505064448-32611
5612: Pipe pair 1507242440-32611
5612: Pipe pair 1-0
无论NBPROC 的值如何,该分析都应该成立,因为直到管道fds 中有1,该进程最终将关闭它自己的stdout 并表现出类似的行为。
这是一个完整的程序,其中包含一些可以通过传入 DBG=1 和一些 PID 标记来确定日志来自哪个进程的工具:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
void comparerIdElection(int (*pipes)[2], int numProc)
{
for (int i = 0; i < NBPROC; ++i)
{
if (numProc!=i)
{
#if DBG == 1
printf("%d: Before close pipe [%d][0](%d)\n",getpid(),i , pipes[i][0]);
#endif
errno = 0;
close(pipes[i][0]);
#if DBG
printf("%d: closed pipe [%d][0](%d) %s\n",getpid(),i, pipes[i][0], strerror(errno));
#endif
} else {
#if DBG == 1
printf("%d: no action on pipe [%d][0](%d) \n",getpid(),i, pipes[i][0] );
#endif
}
if (numProc +1 != i)
{
#if DBG == 1
printf("%d: Before close pipe [%d][1](%d)\n",getpid(),i, pipes[i][1] );
#endif
errno = 0;
close(pipes[i][1]);
#if DBG == 1
printf("%d: close pipe [%d][1](%d) %s\n",getpid(),i, pipes[i][1], strerror(errno) );
#endif
} else {
#if DBG == 1
printf("%d: no action pipe [%d][1](%d) \n",getpid(),i, pipes[i][1] );
#endif
}
}
}
//Generate a random number for my child process
int GenererIdElection ()
{
srand(time(NULL)^getpid()<<16);
return rand()%NBPROC;
}
int main(int argc, char const *argv[])
{
int N = NBPROC, idElection, numProc= -1;
int pere = getpid();
int pipes[N][2];
int pids[N];
char etat ;
printf("%d: Number of process is : %d\n", getpid(), N);
int pipeVal = INT32_MIN;
if(argc == 2) {
pipeVal = atoi(argv[1]);
}
for(int i = 0; i < N; ++i) {
if(pipeVal != INT32_MIN) {
pipes[i][0] = pipes[i][1] = pipeVal;
}
printf("%d: Pipe pair %d-%d\n",getpid(), pipes[i][0], pipes[i][1]);
}
for (int i = 0; i < N; ++i)
{
if(getpid()==pere){
pid_t pid = fork() ;
numProc++;
if(pid == 0) {
// sleep(10);
#if DBG == 1
printf("%d: After fork in the child got fork-pid %d\n", getpid(), pid);
#endif
} else {
pids[numProc] = pid;
#if DBG == 1
printf("%d: After fork in the parent got fork-pid %d numProc %d pids[%d] = %d\n",
getpid(), pid, numProc, numProc, pids[numProc]);
#endif
}
}
}
if (getpid() != pere)
{
etat = 'C';
while(etat == 67)
{
idElection = GenererIdElection();
printf("%d: Election Id %d, numProc %d \n",getpid(), idElection, numProc);
comparerIdElection(pipes, numProc);
etat ='B';
}
printf("%d: Exiting while-loop\n", getpid());
}
if (getpid()==pere)
{
/*On attend la fin de chaque processus
pour mettre un terme au processu pere*/
for (int i = 0; i < N; ++i)
{
errno = 0;
int stat_val;
wait(&stat_val);
#if DBG == 1
printf("%d: WIFEXITED(%d),WEXITSTATUS(%d),WIFSIGNALED(%d),WTERMSIG(%d),WIFSTOPPED(%d),WSTOPSIG(%d),WIFCONTINUED(%d)\n", getpid(), WIFEXITED(stat_val),WEXITSTATUS(stat_val),WIFSIGNALED(stat_val),WTERMSIG(stat_val),WIFSTOPPED(stat_val),WSTOPSIG(stat_val),WIFCONTINUED(stat_val));
#endif
}
}
printf("%d: Exiting process\n", getpid());
exit(EXIT_SUCCESS);
}