Child Process

Child Process

child_process 这个模块可以生成一个子进程。nodejs提供了好几个API,本质上都是调用child_process.spawn():

 1 const spawn = require(‘child_process‘).spawn;
 2 const ls = spawn(‘ls‘, [‘-lh‘, ‘/usr‘]);
 3
 4 ls.stdout.on(‘data‘, (data) => {
 5   console.log(`stdout: ${data}`);
 6 });
 7
 8 ls.stderr.on(‘data‘, (data) => {
 9   console.log(`stderr: ${data}`);
10 });
11
12 ls.on(‘close‘, (code) => {
13   console.log(`child process exited with code ${code}`);
14 });

默认情况下:stdinstdout and stderr 这3个管道会链接在父进程和子进程之间!这使得父子进程数据流的交互畅通无阻。注意:有些程序自身内部利用了I/0

Buffer,但是这并不影响Node.js. 它只是意味着父进程发送数据给子进程不能马上消耗掉而已。

Nodejs为了方便大家使用,提供了很多同步与异步的方法,本文只介绍异步的!异步的优点就是不会阻塞Nodejs的事件循环。

child_process.spawn()child_process.fork()child_process.exec(), and child_process.execFile()

都会返回一个ChildProcess 实例。ChildProcess 实现了NodejsEventEmitter API,允许父进程注册监听函数,在子进程的生命周期内发生指定事件的时候调用。

child_process.exec() and child_process.execFile()还额外提供了callback选项,当子进程终止的时候调用!

Spawning .bat and .cmd files on Windows

child_process.exec() and child_process.execFile()最大的区别无疑是基于的平台不同!

在 Unix-type 这样类型的操作系统上 (Unix, Linux, OSX) ,child_process.execFile() 跑起来更高效,因为他不需要生成一个shell.

然后在Windows上,.bat and .cmd文件是无法离开终端独立执行的,玩Windows的同志只能用child_process.exec()来执行批处理文件。

或者说利用child_process.spawn() 配置项里设置shell.

或者说利用生成一个cmd.exe ,然后传 .bat or .cmd文件名作为参数。这里关键了。一定要带上 /? 不然在你无法生成一个新的shell实例。

 1 // On Windows Only ...
 2 const spawn = require(‘child_process‘).spawn;
 3 const bat = spawn(‘cmd.exe‘, [‘/c‘, ‘my.bat‘]);
 4
 5 bat.stdout.on(‘data‘, (data) => {
 6   console.log(data);
 7 });
 8
 9 bat.stderr.on(‘data‘, (data) => {
10   console.log(data);
11 });
12
13 bat.on(‘exit‘, (code) => {
14   console.log(`Child exited with code ${code}`);
15 });
16
17 // OR...
18 const exec = require(‘child_process‘).exec;
19 exec(‘my.bat‘, (err, stdout, stderr) => {
20   if (err) {
21     console.error(err);
22     return;
23   }
24   console.log(stdout);
25 });
26
27 // Script with spaces in the filename:
28 const bat = spawn(‘"my script.cmd"‘, [‘a‘, ‘b‘], { shell:true });
29 // or:
30 exec(‘"my script.cmd" a b‘, (err, stdout, stderr) => {
31   // ...
32 });

child_process.exec(command[, options][, callback])

  • command <String> The command to run, with space-separated arguments
  • options <Object>
    • cwd <String> Current working directory of the child process
    • env <Object> Environment key-value pairs
    • encoding <String> (Default: ‘utf8‘)
    • shell <String> Shell to execute the command with (Default: ‘/bin/sh‘ on UNIX, ‘cmd.exe‘ on Windows, The shell should understand the -c switch on UNIX or /d /s /c on Windows. On Windows, command line parsing should be compatible with cmd.exe.)
    • timeout <Number> (Default: 0)
    • maxBuffer <Number> largest amount of data (in bytes) allowed on stdout or stderr - if exceeded child process is killed (Default: 200*1024)
    • killSignal <String> (Default: ‘SIGTERM‘)
    • uid <Number> Sets the user identity of the process. (See setuid(2).)
    • gid <Number> Sets the group identity of the process. (See setgid(2).)
  • callback <Function> called with the output when process terminates
  • Returns: <ChildProcess>

1 {
2   encoding: ‘utf8‘,
3   timeout: 0,
4   maxBuffer: 200*1024,
5   killSignal: ‘SIGTERM‘,
6   cwd: null,
7   env: null
8 }

注意回调里的3个参数,如果指定了encoding,那就是String,不然就是buffer。

timeout(如果大于0) 是为了规定子进程的执行时间,如果超过了,这时候就会用到 killSignal 属性了,默认父进程发送的是 ‘SIGTERM‘。

不同于exec(3) POSIX,child_process.exec()    是不会取代已经存在的进程的,使用shell来执行命令。

 

child_process.execFile(file[, args][, options][, callback])

child_process.execFile()和child_process.exec()  很像,除了 child_process.execFile() 执行的时候不会生成新的shell,这个指定的file将会被执行作为新进程。

这一点比child_process.exec() 更有效率。

这里部分一点:

开始学习child_process模块的时候以为spawn可以直接运行命令, 后来发现这是一个小陷阱就拿出来和大家分享一下.

先说下我碰到的情况由于在windos下写的所以根据docs上的例子我就写出了这么一句代码:"require(“child_process”).spawn(“dir”), 这么写是会有错误的,用error接受到的数据是没有此文件. 而用exec就不会有问题,于是得到了以前的猜想.

大家都知道在linux下, ls命令对应的是一个文件, 而在windows下是做为cmd的内置命令的. 所以像我那样写是会报错.

于是我查看了child_process的源码发现spawn是这样定义的var spawn = exports.spawn = function(file, args, options); 也就是说他传入的应该是一个文件, 例如ping, cmd等. 而exec的源码中有一段这样的代码:

 if (process.platform === ‘win32‘) {
file = ‘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];
}

所以想使用内置命令可以直接使用exec或者把spawn改成spawn(“cmd.exe”,["\s", “\c”, “dir”]);

总结起来就是spawn是调用一个文件! 不要被docs上的child_process.spawn(command, [args], [options])中的command给骗了

-----------------------------------------------------------------------------------------------

options.detached

在windows上,设置options.detached 为 true,可以保证父进程退出的时候,子进程还可以运行,子进程拥有自己的console窗口,一旦启动,就不可能停止。

非windows的话,设置options.detached 为 true,子进程将会新进程的控制者,不管父子进程有没有设置detached ,子进程都可以在父进程退出后保存运行!

默认情况下 ,父进程需要等子进程运行完毕才离开,但是我们可以调用child.unref() 来避免发生!这样允许父进程和子进程独立开来,除非他们建立了IPC信道。

看一眼为什么:

针对handle而言,判断loop是否存活只要看loop->active_handles是否大于0,大于0则存活。

具体代码参看 https://github.com/libuv/libuv/blob/v1.x/src/uv-common.h

uv__handle_init, uv__handle_start, uv__handle_stop, uv__handle_ref, uv__handle_unref

比较下面几种情况,可能会有利于理解unref的作用。

第一种

var timer1 = setTimeout(function(){
  console.log(new Date, 1);
}, 1000);
// setTimeout=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 1  => callback()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 1
// 2: active_handles > 0 =>  loop()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 1
// goto 2

第二种

var timer1 = setTimeout(function(){
  console.log(new Date, 1);
}, 1000);
// setTimeout=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

timer2.unref();
// uv_unref(timer2) active_handles = 1

// ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 0  => callback()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 0
// active_handles == 0 =>  exit_process

第三种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 1  => callback() => uv_timer_start(timer1) active_handles = 2
// timer2 timeout => uv_timer_stop(timer2) active_handles = 1  => callback() => uv_timer_start(timer2) active_handles = 2
// goto 1

第四种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

timer2.unref()
// uv_unref(timer2) active_handles = 1

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 0  => callback() => uv_timer_start(timer1) active_handles = 1
// timer2 timeout => uv_timer_stop(timer2) active_handles = 1  => callback() => uv_timer_start(timer2) active_handles = 1
// goto 1

第五种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

timer1.unref()
// uv_unref(timer1) active_handles = 0

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 1

timer2.unref()
// uv_unref(timer2) active_handles = 0

// ative_handles == 0 => exit process

--------------------------------------------------------------------------

时间: 2024-10-15 00:52:49

Child Process的相关文章

Linux parent process and child process when &#39;sudo&#39;

如果在一般用户下如user,执行sudo命令,会产生两个进程. ps -ef | grep Container root 4305 643 0 16:37 pts/39 00:00:00 sudo ./ContainerCompilation.shroot 4306 4305 0 16:37 pts/39 00:00:00 /bin/bash ./ContainerCompilation.sh 4306进程的父进程是4305,也就是说unix先生成4305进程将命令交给root,然后产生4306

innobackupex:Error:xtrabackup child process has died at /usr/bin/innobackupex

使用innobackupex进行数据库备份,报如下错误:innobackupex --compress --parallel=4  --user=root  --password=yoon /export/backup/xtrabackup_56 version 2.1.9 for MySQL server 5.6.17 Linux (x86_64) (revision id: 744)xtrabackup: uses posix_fadvise().xtrabackup: cd to /var

Linux Apache error 日志 提示[warn] child process 7751 still did not exit, sending a SIGTERM

Apache吃空内存,频繁宕机 在部署一套内网测试环境时,频繁宕机,开机后不断的吃内存,重启apache之后内存占用会不停的上涨,直到swap用完,直到死机,由于是内网环境,服务器并发和压力都很小. 查看apache错误日志,报大量类似错误: [Tue Feb 14 14:49:28 2012] [warn] child process 7751 still did not exit, sending a SIGTERM [Tue Feb 14 14:49:30 2012] [error] ch

Mongodb中经常出现的错误(汇总)child process failed, exited with error number

异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 异常处理汇总-数据库系列  http://www.cnblogs.com/dunitian/p/4522990.html 情况不唯一,这边只能当参考,大致就是这么几种解决思路 child process failed, exited with error number 1 child process failed, exited with error number 48 chi

wireshark:Couldn&#39;t run /usr/bin/dumpcap in child process: Permission denied

When start wireshark, I met an error like: 引用 Couldn't run /usr/bin/dumpcap in child process: Permission denied Solution: 引用 sudo apt-get install libcap2-bin wireshark sudo chgrp myusername /usr/bin/dumpcap sudo chmod 750 /usr/bin/dumpcap sudo setcap

Child Process模块

目录 exec() execSync() execFile() spawn() fork() send() 参考链接 child_process模块用于新建子进程.子进程的运行结果储存在系统缓存之中(最大200KB),等到子进程运行结束以后,主进程再用回调函数读取子进程的运行结果. exec() exec方法用于执行bash命令,它的参数是一个命令字符串. var exec = require('child_process').exec; var ls = exec('ls -l', funct

ERROR: child process failed, exited with error number 51

1.ERROR: child process failed, exited with error number 51 异常的关闭再次重启时容易产生这样的错误,首先删除mongod.lock 文件  修复  再次启动 . 2.Error parsing INI config file: unrecognised option 'configdb' 仔细看下图操作,mongos启动时用mongos而不是mongod 原文地址:https://www.cnblogs.com/tszc/p/856819

liunx启动mongodb报错 mongodb child process failed, exited with error number 14

mongodb启动报错:mongodb child process failed, exited with error number 14,查看mongodb.cnf日志,发现其中有一条:Too many open files at src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp 79. 设置ulimit -n 10000.完美! 原文地址:https://www.cnblogs.com/shilang/p/1019748

Git bash Error: Could not fork child process: There are no available terminals (-1)

错误信息:Error: Could not fork child process: There are no available terminals (-1) 截图如下: 解决办法: (1)使用cmd命令tasklist,找到git bash的进程 (2)找到红色标记处 (3)执行命令(taskkill /pid 9872 -t -f)将其杀死即可 参考问题解决链接:Git bash Error: Could not fork child process: There are no availa