Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

child_process中的exec与spawn #11

Open
39Er opened this issue May 16, 2017 · 0 comments
Open

child_process中的exec与spawn #11

39Er opened this issue May 16, 2017 · 0 comments

Comments

@39Er
Copy link
Owner

39Er commented May 16, 2017

exec()官方示例:

const exec = require('child_process').exec;
exec('cat *.js bad_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

spawn()官方示例:

const spawn = require('child_process').spawn;
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

exec() 和 spawn() 的区别:

    1. 从return来看,exec()返回string或buffer;spawn()返回stream
    1. spawn()是“异步的异步”,也就是说在子进程开始执行时,它就开始从一个流将数据从子进程返回给Node;exec()是“同步的异步”,意思是虽然exec()是异步的,它一定要等到子进程结束然后尝试一次性返回所有的缓存数据,如果exec的buffer体积设置的不够大,它将会以一个“maxBuffer exceeded”错误失败告终。
    1. exec()默认参数:

         const defaults = {
           encoding: 'utf8',
           timeout: 0,
           maxBuffer: 200*1024,
           killSignal: 'SIGTERM',
           cwd: null,
           env: null
         };
      

spawn()默认参数:

	const defaults = {
	  cwd: undefined,
	  env: process.env
	};

exec是对execFile的封装,execFile又是对spawn的封装。

详见源码:

exports.exec = 
	function(command /*, options, callback*/) {
  		var opts = normalizeExecArgs.apply(null, arguments);
  		return exports.execFile(opts.file,
                          		opts.args,
                          		opts.options,
                          		opts.callback);
};

    exec对于execFile的封装是进行参数处理

    处理的函数:

    normalizeExecArgs

if (process.platform === 'win32') {
    file = process.env.comspec || 'cmd.exe';
    args = ['/s', '/c', '"' + command + '"'];
    // Make a shallow copy before patching so we don't clobber the user's
    // options object.
    options = util._extend({}, options);
    options.windowsVerbatimArguments = true;
  } else {
    file = '/bin/sh';
    args = ['-c', command];
  }

感觉这个处理和我的想法很像...

将简单的command命名做一个,win和linux的平台处理。

此时execFile接受到的就是一个区分平台的command参数。

然后重点来了,继续debug,execFile中:

var options = {
    encoding: 'utf8',
    timeout: 0,
    maxBuffer: 200 * 1024,
    killSignal: 'SIGTERM',
    cwd: null,
    env: null
};

有这么一段,设置了默认的参数。然后后面又是一些参数处理,最后调用spawn方法启动子进程。

上面的简单流程就是启动一个子进程。到这里都没有什么问题。

继续看,重点又来了:

用过子进程应该知道这个child.stderr

下面的代码就解答了为什么子进程会挂掉。

child.stderr.addListener('data', function(chunk) {
    stderrLen += chunk.length;

    if (stderrLen > options.maxBuffer) {
      ex = new Error('stderr maxBuffer exceeded.');
      kill();
    } else {
      if (!encoding)
        _stderr.push(chunk);
      else
        _stderr += chunk;
    }
});

逻辑就是,记录子进程的log大小,一旦超过maxBuffer就kill掉子进程。

原来真相在这里。我们在使用exec时,不知道设置maxBuffer,默认的maxBuffer是200K,当我们子进程日志达到200K时,自动kill()掉了。

这也正是exec()与spawn()最大的区别: exec()限制了maxBuffer默认为200k,超过时会自动kill子进程,并报"maxBuffer exceeded"异常

参考文献:https://www.hacksparrow.com/difference-between-spawn-and-exec-of-node-js-child_process.html

使用node子进程spawn-exec踩过的坑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant