节点pty.js产生一个进程本身产生subprocess,并且当节点被杀时subprocess不会死

使用Ubuntu 13.10并运行节点v0.10.0。 我正在使用pty.js v0.2.4来生成一个程序(需要在交互式环境中运行)。 该程序是用C语言编写的,并自行分发一个subprocess。

我写了一个C程序(我称之为“forktest”)的一个非常简化的版本,它具有产生这个问题的最低要求,并包含以下内容:

#include <unistd.h> #include <sys/types.h> #include <errno.h> #include <stdio.h> #include <sys/wait.h> #include <stdlib.h> int main(void) { pid_t childPID; childPID = fork(); if(childPID >= 0) { if(childPID == 0) { signal(SIGHUP, SIG_IGN); while(1) { printf("Child Process\n"); fflush(stdout); usleep(500000); } } else { printf("Parent process\n"); fflush(stdout); getchar(); } } else { printf("Fork failed, exiting\n"); return 1; } return 0; } 

我还编写了一个用coffeescript(test.coffee)编写的最小节点脚本,它运行程序,如下所示:

 pty = require 'pty.js' command = './forktest' example = pty.spawn command, null example.on 'data', (data) -> console.log data example.on 'exit', -> console.log 'example exited' 

在运行节点脚本“coffee test.coffee”时,会看到输出(“Parent Process”和“Child Process”)。 此外,进程的层次结构在ps('ps faux')中正确显示:

 lightdm \_ /usr/bin/X -core :0 -auth /var/run/lightdm/root/:0 -no \_ lightdm --session-child 12 21 \_ init --user \_ gnome-terminal \_ bash \_ node /usr/local/bin/coffee test.coffee \_ ./forktest \_ ./forktest 

但是当退出coffeescript(用ctrl-c)时,父叉testing过程也按照预期退出,但是子testing过程被留下,'ps faux'的输出如下所示:

 lightdm \_ /usr/bin/X -core : \_ lightdm --session- \_ init --user \_ ./forktest 

我调查了这是因为在subprocess中挂起信号被忽略:

 signal(SIGHUP, SIG_IGN); 

如果我使用节点中的child_process模块​​的产卵:

 {spawn} = require 'child_process' command = './forktest' example = spawn command example.stdout.on 'data', (data) -> console.log String data example.on 'exit', -> console.log 'example exited' 

并以同样的方式退出coffeescript应用程序(ctrl-c),当它退出forktest的父进程和subprocess消失,所以它看起来与pty.js如何以某种方式工作有关。

我不能编辑(删除忽略信号),并分发原来的C程序给用户,所以我正在寻找一种解决方法。

一个解决方法是捕捉节点的中断信号:

 process.on 'SIGINT', (data) -> # kill the parent / child process manually 

我也许应该这样做。但是,如果咖啡标记过程被杀害,而不是中断,它仍然会遭受同样的问题,并把孩子留下。 据我所知,没有办法在节点中捕捉SIGKILL?

是否有一个原因,当pty.js生成的程序的subprocess在节点被杀时不被销毁?

您正在遇到一个POSIX系统如何处理查杀过程的细微问题。

当进程被杀死时,内核重新定义父进程。 与孩子退出并通知其父母不同,如果父母程序在退出时不向孩子发送信号,则孩子不会收到通知 。 内核只是重新定义它的父id到init进程。

因此,默认情况下, 当父母退出时,没有任何孩子退出 。 那么当父母离开时,孩子们怎么会被杀呢? 那么,每个进程都有一个进程组ID。 默认情况下,它在开始时是父级的进程组标识。 如果进程组由于进程死亡而成为孤儿 ,那么内核可能会向进程组的所有成员发送SIGHUPSIGCONT 。 这给孩子们一个退出的机会,但他们不需要。 如果他们不退出,那么他们是孤立的进程,并且他们的父id被设置为init进程。

巧合的是,这是如何分叉创build一个守护进程的作品。

因此,要解决您的问题,请确保孙辈是同一个进程组ID的一部分。 这应该让他们收到杀死孩子过程的信号。 但是,如果他们忽略了这个信号,那么你实际上只有一个select。

产生一个包装程序,除了产生实际的过程外,什么都不做,并且在“真正的”父母和“真正的”孩子之间来回传递。 然后,如果SIGHUP被发送到你的包装,发送SIGKILL给孩子强制退出。