【问题标题】:Spawn and kill a process in node.js在 node.js 中生成并终止一个进程
【发布时间】:2013-09-12 17:34:56
【问题描述】:

我正在尝试在 javascript 中生成一个进程,并在一段时间后将其终止(出于测试目的)。

最后,进程将是一个无限循环,我需要在指定时间使用不同的参数重新启动,所以我认为生成进程并杀死它是最好的方法。

我的测试代码是:

var spawn=require('child_process').spawn
, child=null;

child=spawn('omxplayer', ['test.mp4'], function(){console.log('end');}, {timeout:6000});
console.log('Timeout');
setTimeout(function(){
    console.log('kill');
    child.kill();
}, 1200);

child.stdout.on('data', function(data){
    console.log('stdout:'+data);
});

child.stderr.on('data', function(data){
    console.log('stderr:'+data);
});

child.stdin.on('data', function(data){
    console.log('stdin:'+data);
});

结果是:

#~$ node test.js
Timeout
kill

但是我仍然需要发送 ctrl+C 来结束程序。我错过了什么?

在 Raspbian 节点 0.10.17 上,omxplayer 是一个二进制文件(视频播放器)。

我试过了:

  • 在应用中添加了chmod +x
  • 以 root 身份启动。
  • 暂停子进程的标准输入。在 kill 命令中使用所有与终止相关的信号。

我还在应用运行时启动了ps 命令:

2145    bash
2174    node
2175    omxplayer
2176    omxplayer.bin
2177    ps

所以omxplayer是一个包装器,它结束时不杀死它的子进程,有什么办法可以得到被包装进程的pid?

还是咬牙切齿,试试这个:

spawn('kill', ['-QUIT', '-$(ps opgid= '+child.pid+')']);

我认为这会杀死 omxplayer 的所有孩子,我不知道这样使用 spawn 是错误的还是代码不起作用。

我所做的最后一个编辑是很好的答案,但必须稍微编辑一下。

我创建了一个 sh 文件(具有执行权限),如下所示:

PID=$1
PGID=$(ps opgid= "$PID")
kill -QUIT -"$PGID"

我是这样开始的:

execF('kill.sh', [child.pid], function(){
    console.log('killed');
});

而不是child.kill

我不确定这是否是最好的方法,也不确定代码是否干净,但它确实有效。

我会接受任何以更简洁的方式,甚至更好,而无需执行文件的答案。

【问题讨论】:

  • Raspbian 上的树莓派(不确定版本),节点 0.10.2。我会尝试更新节点。
  • 也许是有特权的东西。尝试使用 root 访问运行您的节点脚本。
  • 已将节点更新到 0.10.17 并尝试以 root 身份运行,但均未正常运行。
  • this 可能是 omx 的问题。尝试更新它。
  • 我猜 omxplayer 可执行文件是一个将视频输出到屏幕的进程的包装器,它不会关闭它产生的视频进程。

标签: javascript node.js


【解决方案1】:

参考this discussion

一旦你开始在标准输入上监听数据,节点将等待标准输入上的输入,直到它被告知不要。当用户按下 ctrl-d(表示输入结束)或程序调用 stdin.pause() 时,节点停止等待 stdin。

节点程序不会退出,除非它无事可做或等待。发生的事情是,它正在等待标准输入,因此永远不会退出。

尝试将您的 setTimeout 回调更改为

console.log('kill');
child.stdin.pause();
child.kill();

我希望这会奏效。

【讨论】:

  • 这个也试过了,都评论了“关于数据”,我还是要等视频结束。
  • 试试 child.kill('SIGKILL') 看看是否可行。 child.kill() 发送 SIGHUP 只是告诉控制终端已关闭或控制进程已关闭。我的假设是 SIGKILL 肯定会杀死它。
  • 试过 SIGHUP、SIGTERM 和 SIGKILL,消息 kill 被打印,但视频仍在播放。
  • 试过 SIGKILL、SIGTERM、SIGABRT、SIGHUP、SIGINT 和 SIGQUIT,都没有做任何事情,甚至一个接一个启动。
  • 当我尝试生成其他命令(如 pidgin)的子代时,代码运行良好。但它不与omxplayer?嗯。
【解决方案2】:

有一个非常简洁的 npm 包,名为 tree-kill,它可以轻松有效地完成此任务。它会杀死子进程,该子进程可能已经启动的所有子进程。

var kill  = require('tree-kill');
const spawn = require('child_process').spawn;

var scriptArgs = ['myScript.sh', 'arg1', 'arg2', 'youGetThePoint'];
var child = spawn('sh', scriptArgs);

// some code to identify when you want to kill the process. Could be
// a button on the client-side??
button.on('someEvent', function(){
    // where the killing happens
    kill(child.pid);
});

【讨论】:

  • 谢谢。这对我有用,似乎是一个很好的选择。
  • 这对我有用,而 child.kill() 没有。使用 NodeJS v14.15.1,Windows 10
  • 我对此赞不绝口 - 在浪费了 5 个小时之后,这是在 Windows 7 上对我有用的唯一解决方案!
  • 这对我来说是唯一可行的解​​决方案。谢谢!
【解决方案3】:

我在使用 omxplayer 时遇到了与您完全相同的问题,this blog post 中的解决方案对我有用。

var psTree = require('ps-tree');

var kill = function (pid, signal, callback) {
    signal   = signal || 'SIGKILL';
    callback = callback || function () {};
    var killTree = true;
    if(killTree) {
        psTree(pid, function (err, children) {
            [pid].concat(
                children.map(function (p) {
                    return p.PID;
                })
            ).forEach(function (tpid) {
                try { process.kill(tpid, signal) }
                catch (ex) { }
            });
            callback();
        });
    } else {
        try { process.kill(pid, signal) }
        catch (ex) { }
        callback();
    }
};

// elsewhere in code
kill(child.pid);

【讨论】:

    【解决方案4】:

    为什么不直接在标准输入管道中发送 'q' 值?它会杀死 omxplayer 进程。

    【讨论】:

    • 你能解释一下吗?
    • 这是 omxplayer 的自然行为。当您在 shell 中启动它时,该进程正在等待诸如“q”之类的快捷方式,它将关闭程序。
    • 这是一个非常干净的解决方案,也适用于其他程序。 f.stdin.write("q\n");因为需要某种输入。
    【解决方案5】:

    您已生成一个已成功终止的子进程。但是,您的主线程仍在执行,这就是您必须按 Ctrl+C 的原因。

    【讨论】:

    • 如果只是主进程仍在运行,我会很高兴,但子进程显然仍在运行,因为视频仍在播放,直到我发送 ctrl+C。
    【解决方案6】:

    最后,我找到了不用脚本的方法:

    exec('pkill omxplayer', function(err, stdout, stderr){
        if (stdout){console.log('stdout:'+stdout);}
        if (stderr){console.log('stderr:'+stderr);}
        if (err){throw err;}
        //...
    }
    

    【讨论】:

      【解决方案7】:

      尝试使用here中的child_process.execFile()方法。

      child_process.execFile() 函数类似于 child_process.exec() 只是它不生成 shell。相当, 指定的可执行文件直接作为新进程生成 使其比 child_process.exec() 更高效。

      它适用于我的情况。

      【讨论】:

        【解决方案8】:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-25
          • 2012-12-29
          • 1970-01-01
          • 1970-01-01
          • 2010-11-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-30
          相关资源
          最近更新 更多