使用自管道,我怎样才能避免事件循环在read()上停止?

我正在尝试利用自管技巧来获得我的应用程序的可移植实现(跨Solaris,MacOSX,Linux,BSD)。

所以除了stderrstdout的两个管道,我用它来获取分叉子的输出(我在子节点中不使用exec ,子节点执行与父节点相同的代码),我有信号管道( enum {SIG_PIPE, STDOUT_PIPE, STDERR_PIPE, MAX_PIPE}提供符号名称)。

  • 在调用handle_child_output()之前,在管道上设置O_NONBLOCK
  • 子进程具有stderrstdout管道的写入端,并继续使用printf()和朋友,有效地写入每个相应的管道( setvbuf用于关闭子进程内的缓冲)。

接下来的代码有点冗长,一般的自管技巧也是如此。 它已经是浓缩forms。 因此,让我试着解释一下应该发生什么以及卡在哪里。

需要收集退出状态,我必须能够找出孩子是通过信号终止还是通过退出终止。 在其他地方处理这些条件。 有意义的是handle_child_output()返回pstatus指向的int内的子pstatus的退出代码。

handle_child_output()的外部dowhile循环将设置要在select调用中使用的FD_SET 。 它添加了信号管道读取端以及来自子节点的stderrstdout管道的读取端。

然后if(FD_ISSET(fds[SIG_PIPE], &rd))检查信号管道是否包含任何新的并排出它,处理任何潜在的信号。

之后的for循环遍历剩余的文件描述符,以查看是否有任何stdio处理,然后通过parroting它在父对象的stdio通道上得到的内容进行处理。

第二个循环中的读取调用是停止的位置。

症状是父母卡在读取调用中:

 (gdb) bt 1 #0 0x00007f2daaa9e530 in __read_nocancel () from /lib64/libpthread.so.0 

就好像在读取信号管和其他管道之间存在竞争条件。 当文件描述符被检查时,孩子似乎已经退出并因此最终成为僵尸,因为父母仍然停留在read()并且永远不会到达wait()调用。

我究竟做错了什么? 添加if(exitloop) break;合法if(exitloop) break;for循环之前突破外部dowhile循环? 在我看来,这可能会在管道中留下未读的内容,不是吗?


 #define __MAX__(x,y) ((x) > (y) ? (x) : (y)) int childpid; typedef enum { READ, WRITE, BOTH } pipefd_type_t; static void avoid_zombie(int* pstatus) { int temp; pstatus = (pstatus) ? pstatus : &temp; if(0 > childpid && 0 != childpid) { kill(childpid, SIGKILL); /* kill the child */ } wait(pstatus); /* wait to avoid lingering zombies */ } static void select_signal_handler(int sig) { int savedErrno = errno; const int sigw = sig; if(0 > write(sigpipe[WRITE], &sigw, sizeof(sigw))) { avoid_zombie(NULL); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } errno = savedErrno; } void handle_child_output(int *pstatus) { enum {SIG_PIPE, STDOUT_PIPE, STDERR_PIPE, MAX_PIPE}; fd_set rd; int ready, n = 0, fds[MAX_PIPE]; size_t i, exitloop, sigint; struct sigaction sa; struct { int sig; struct sigaction oldsa; } old_sigactions[3]; old_sigactions[0].sig = SIGINT; old_sigactions[1].sig = SIGCHLD; old_sigactions[2].sig = SIGQUIT; /* fds have been initialized at this point (read-ends) */ for(i = 0; i  sigaction(old_sigactions[i].sig, &sa, &old_sigactions[i].oldsa)) { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } } do { sigint = 0; exitloop = 0; FD_ZERO(&rd); for(i = 0; i = FD_SETSIZE) { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } FD_SET(fds[i], &rd); n = __MAX__(n, fds[i]); } while(0 > (ready = select(n+1, &rd, NULL, NULL, NULL))) if (EINTR == errno) continue; if(0 > ready) { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } if(FD_ISSET(fds[SIG_PIPE], &rd)) { do { /* drain the signal pipe */ int sig = -1; if(0 > read(fds[SIG_PIPE], &sig, sizeof(sig))) { if(EAGAIN == errno) break; else { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } } switch(sig) { case SIGINT: if(0 > childpid && 0 != childpid) { kill(childpid, SIGINT); /* pass to child */ wait(pstatus); } sigint++; exitloop++; break; case SIGCHLD: exitloop++; break; } } while(1); } for(i = STDOUT_PIPE; i  bytesRead) { /* Retry (inner do-while loop) if we get an EAGAIN */ if(EAGAIN == errno) break; /* fatal error */ avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } if(STDOUT_PIPE == i) outchan = STDOUT_FILENO; bytesWritten = write(outchan, buf, bytesRead); if(0 > bytesWritten) { /* Retry if we get an EAGAIN */ if(EAGAIN == errno) { size_t tries; for(tries = 0; tries  bytesWritten) && (EAGAIN == errno)) continue; break; } } if(0 > bytesWritten) { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } } if(bytesWritten  bytesWritten2) || (bytesWritten2 != bytesToWrite)) { /* fatal error */ avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } } } while(1); } } } while(0 == exitloop); /* restore old signal handlers */ for(i = 0; i < sizeof(old_sigactions)/sizeof(old_sigactions[0]); i++) { if (sigaction(old_sigactions[i].sig, &old_sigactions[i].oldsa, NULL) == -1) { avoid_zombie(pstatus); _exit(EXIT_FAILURE); /* actual code also shows error etc */ } } avoid_zombie(pstatus); }