关于Nodejs child_process中的exec

关于Nodejs child_process中的exec

我们不妨设现在有一个需要运行2.3s左右的程序。它在同目录下。./1可以执行它, 我们给它重定向输入输出,但是这个程序有时间限制,一旦超过时间限制我们需要立马终结它。

	var cp = require('child_process');
	var child = cp.exec(__dirname + '/1 < in > tempout', function(err, stdout, stderr) {
		clearTimeout(timer);
	});
	var timer = setTimeout(function() {
		child.kill();
	}, 1000);

然后我尝试了这样一个代码,发现无论如何程序还是会在2.3s后正常输出到tempout。

	var cp = require('child_process');
	cp.exec(__dirname + '/1 < in > tempout', {timeout: 1000, killSignal: 'SIGTERM'}, function(err, stdout, stderr) {
	});

仍然和上述情况相同。。不知道为啥,跪求解答。


5 回复

在Node.js中使用child_process模块的exec方法来执行外部程序时,有时会遇到一些问题,比如你提到的时间限制问题。你已经尝试了一些方法,但似乎没有达到预期的效果。接下来,我会详细解释如何正确地使用exec方法,并且添加时间限制以确保外部程序能够在规定时间内结束。

解释

首先,exec方法用于创建一个shell来执行命令,并等待该命令完成。如果命令执行超时,你可以通过设置timeout选项并结合killSignal来强制终止进程。

示例代码

var cp = require('child_process');

// 定义外部程序的命令
const command = `${__dirname}/1 < in > tempout`;

// 创建子进程并设置超时时间和终止信号
const child = cp.exec(command, { timeout: 1000, killSignal: 'SIGTERM' }, (err, stdout, stderr) => {
    if (err) {
        console.error(`执行出错: ${err}`);
    } else {
        console.log(`标准输出:\n${stdout}`);
        console.error(`标准错误:\n${stderr}`);
    }
});

// 监听超时事件,确保即使超时也能终止进程
child.on('timeout', () => {
    console.log("超时,正在终止进程...");
    child.kill();
});

// 监听错误事件
child.on('error', (err) => {
    console.error(`进程启动出错: ${err}`);
});

关键点解释

  1. 命令字符串:构建正确的命令字符串很重要,确保路径和文件名正确无误。
  2. timeoutkillSignaltimeout 设置了进程的最大执行时间(毫秒),killSignal 指定了在超时时使用的信号。默认情况下,SIGTERM 是一个合理的信号,表示请求进程终止。
  3. 事件监听:通过监听timeouterror事件,我们可以更好地处理异常情况,确保进程能够被正确地终止。

注意事项

  • 确保in文件存在并且路径正确。
  • 如果你的外部程序需要更长时间才能完成,你可能需要调整timeout的值。
  • SIGKILL9)是一个更强力的信号,通常用于强制立即终止进程。然而,它不会给进程任何清理的机会。

希望这段代码能帮助你解决时间限制问题。如果你还有其他疑问或需要进一步的帮助,请随时提问!


exec只是个传话器,并不负责实际的内容运行。

nodejs调用exec告诉OS: 需要运行OS的哪条指令。然后OS开动一个进程去干活,干完活把结果发回nodejs。 nodejs本身也属于OS其中的一条进程。

个人猜测:exec 会启动一个 shell, 当 kill 的时候实际上是在 kill 这个 shell, 所以 shell 启动的子进程不会被正确地结束。 因此建议使用 spawn 代替 exec.

我试了试,发现情况是这样的, node.jpg 当执行exec的时候,node的子进程是bash,bash的子进程是test.sh>log,本来我以为到这里就结束了,然而test.sh>log还有另外一个子进程test.sh。当timeout的时候。nodekill掉了bash,因为test.sh>log是bash的子进程,因此它也被kill掉了。但是test.sh>log的子进程test.sh却没有跟着被kill,它的父进程变成了1。这可能就是导致脚本继续执行下去的原因了,至于为何会这样,需要看node源码和linux系统编程的,具体我也不懂。

根据你的描述,child_process.exec 方法并不直接支持通过配置项来设置超时并强制终止子进程的功能。你需要手动处理超时逻辑。你可以通过 setInterval 或者 setTimeout 来实现超时检测,并在超时时使用 child.kill() 来终止子进程。

下面是修改后的示例代码:

var cp = require('child_process');

// 创建子进程
var child = cp.exec(__dirname + '/1 < in > tempout');

// 设置超时时间
var timeout = 1000; // 1秒

// 超时定时器
var timer = setTimeout(function() {
    console.log("超时!终止子进程...");
    child.kill('SIGTERM'); // 使用 SIGTERM 信号终止子进程
}, timeout);

// 监听子进程的错误和结束事件
child.on('error', function(err) {
    console.error("子进程发生错误:", err);
});

child.on('exit', function(code, signal) {
    clearTimeout(timer); // 清除超时定时器
    console.log("子进程已退出,退出码:", code, "退出信号:", signal);
});

在这段代码中,我们使用 clearTimeout(timer) 在子进程正常退出时清除定时器,防止超时逻辑误杀子进程。同时,我们在子进程开始时立即启动一个定时器,如果超时则调用 child.kill() 来终止子进程。

注意 kill 方法默认发送 SIGKILL 信号,但在这里我们指定了 SIGTERM 信号,这是一种更温和的终止方式,允许进程有机会进行清理操作。如果你希望强制终止进程,可以改用 SIGKILL

回到顶部