使用Ubuntu 13.10并运行节点v0.10.0。我正在使用pty.js v0.2.4来生成一个程序(需要在交互式环境中运行)。该程序是用C语言编写的,并且本身就是一个子进程。
我已经编写了一个非常简化的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)时,父forktest进程也会按预期退出,但是子forktest进程会被抛弃,'ps faux'的输出则如下所示:
lightdm
\_ /usr/bin/X -core :
\_ lightdm --session-
\_ init --user
\_ ./forktest
我调查过这是因为在子进程中忽略了挂起信号:
signal(SIGHUP, SIG_IGN);
如果我在node:
中使用child_process模块中的spawn{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的父进程和子进程时消失,所以它看起来与pty.js如何以某种方式工作有关。
我无法编辑(删除忽略信号)并将原始C程序分发给用户,所以我正在寻找解决此问题的方法。
一种解决方法是使用以下方法捕获节点的中断信号:
process.on 'SIGINT', (data) ->
# kill the parent / child process manually
无论如何我应该做什么..但如果coffeescript过程被杀死而不是被打断,它仍将遭受同样的问题并让孩子落后。据我所知,没有办法在节点中捕获SIGKILL?
当节点被杀死时,有没有原因导致用pty.js生成的程序的子进程没有被销毁?
答案 0 :(得分:2)
您正遇到一个关于POSIX系统如何处理终止进程的细微问题。
当进程被终止时,内核redefines who the parent is。与子进程退出并通知其父进程不同,如果父进程在退出时没有向子进程发送信号,则子进程不会收到通知。内核只是将其父ID重新定义为init进程。
因此,默认情况下当父级退出时,不会退出任何子级。那么当父母退出时,孩子怎么会被杀?好吧,每个进程都有一个进程组ID。这默认为启动时它的父进程组ID。如果进程死亡的进程组becomes orphaned,那么内核可能会向进程组的所有成员发送SIGHUP
和SIGCONT
。这让孩子有机会退出,但他们不必这样做。如果他们没有退出,那么他们就是孤立的进程,并将他们的父id设置为init进程。
巧合的是,这就是分支创建守护进程的方法。
因此,要解决您的问题,请确保孙子是同一进程组ID的一部分。这应该让他们收到杀死子进程的信号。但是,如果他们忽略了信号,那么你真的只有一个选项。
生成一个包装器程序,除了生成你的实际进程之外什么都不做,并在&#34; real&#34;之间来回传递所有内容。父母和&#34;真实&#34;儿童。然后,如果SIGHUP
被发送到您的包装器,请向该子项发送SIGKILL
以强制退出。