终止 bash 脚本不会终止子进程

Killing a bash script does not kill child processes

我写了一个测试脚本,它运行另一个脚本来启动服务器进行测试。当测试完成时,一条 SIGKILL 消息被发送到服务器进程,但是当 运行 测试脚本再次出现时,服务器抛出 EADDRINUSE 错误(我在 node.js 环境)这意味着服务器正在尝试挂载的端口当前正在使用中。我们试图用 SIGKILL 杀死的进程仍然是 运行。我不认为这是一个特定于节点的问题,而是我缺乏关于 bash 进程如何工作的教育。

这里有一些细节,这是我的启动脚本 scripts/start-node.sh:

#!/bin/bash

node_modules/.bin/babel-node --stage 0 index.js

这是我的节点服务器 index.js(我没有编写任何 process 事件侦听器):

Http.createServer(…).listen(PORT, () => console.log(`Server listening on ${PORT}`))

启动脚本由节点child_process模块控制:

var child = child_process.spawn('scripts/start-node.sh')
// Later…
child.kill('SIGKILL')

在您的服务器中有一个处理信号的处理程序

server.js

 var http = require('http');

function handleRequest(request, response){
    response.end("Hello");
}
var server = http.createServer(handleRequest);

server.listen(3000, function(){console.log("listening...")});
process.title="testserver"

process.on('SIGQUIT', function() {
  server.close()
});

startup.sh

 #!/bin/bash
 node server.js & echo "started"

然后有一个"shutdown.sh".

shutdown.sh

#!/bin/bash
pkill -QUIT testserver & echo "stoped"

如果您在 "start-node.sh" 中生成多个进程,它将终止所有具有该名称的进程。这样你就可以在关闭时有一些清理代码,例如关闭所有连接等

在你的测试运行器中你可以做

var exec = require('child_process').exec;
exec("./start.sh")
// later...
exec("./stop.sh")

要杀死一个 child 进程及其所有 children 你可以使用 process.kill 和一个否定 pid (杀死一个进程组)

var child = child_process.spawn('scripts/start-node.sh', {detached: true})
// Later…
process.kill(-child.pid, 'SIGKILL');

请参阅 options.detached

child_process 文档的详细信息

On non-Windows, if the detached option is set, the child process will be made the leader of a new process group and session.

这里引用了 man 2 kill 的一部分以了解一些细节:

If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid.


另一个选项可能是在您的 shell 脚本中使用 trap 来拦截信号并杀死所有 children 并使用 child.kill('SIGTERM') 来自 node (因为 SIGKILL 不会被 trap 拦截)

#!/bin/bash

trap 'kill $(jobs -p)' EXIT
node_modules/.bin/babel-node --stage 0 index.js

简单地说:

#!/bin/bash
if pgrep -x "node" > /dev/null
then
mv -f  /usr/local/bin/node /usr/local/bin/node.1
killall  node
mv -f /usr/local/bin/node.1 /usr/local/bin/node
which node
else
echo "process node not exists"
fi

node is creating child process every-time we kill it. So it's not possible to kill the process from kill,pkill or killall commands. So we are removing node command to make forking process fail and then we kill the process.Finally we restore the node command.