在了解child_processz之前,我们先来了解几个计算机操作系统中的基本概念,以及他们之间存在的关系。
cup: 计算机包含五大基本硬件运算器、控制器、存储器、输入,输出设备。运算器和控制器集成为中央处理单元即CPU(Central Processing Unit),其主要作用是执行一系列指令运算然后将结果写回。
进程: 进程是系统进行资源分配和调度的基本单位,同一时间在单个CUP上只能有一个进程运行,它会占用独立的内存。但是我们可能会想到,计算机运行的时候肯定不只是只有一个进程在运行。在现代操作系统中,所有进程会轮流使用cpu,但是由于cpu的运行效率极高,可以在多个任务间快速切换,给我们的感觉就好像多个任务在并发执行。
线程: 线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,线程共享该进程所拥有的全部资源,但是当有其中一个线程使用某一块共享内存的时候,其他线程必须等待它结束后,才能使用这一块内存。
node
我们知道node是单线程运行的,当我们用node app.js启动node服务的时候会在服务器上运行一个node的进程,我们的js代码只会在其中的一个线程运行。在node的设计中就是将耗时长的操作代理给操作系统或者其他线程,这部分操作就是磁盘I/O和网络I/O等常见的异步操作,并且将这些耗时的操作从主线程上脱离。虽然node从语言层面不支持创建线程,但是我们可以通过child_process模块创建一个新的进程完成耗时耗费资源的操作,比如说要执行一段上传或下载大文件的shell脚本,然后将执行结果回传给主线程。
child_process创建子进程
使用child_process创建子进程的方式主要有以下四种:
exec、execFile、spawn、fork、
关于其各自的用法请自行查阅文档
process_child文档
这里我们只对一些重要的需要注意的点,以及他们各自的使用场景做一个总结
exec
child_process.exec(command, {...config}, (err, stdout, stderr) => {})
在exec方法的第二个参数中有这么一项配置即{maxBuffer: 200 * 1024}。它表示stdout、stderr 允许的最大输出大小(以 byte 为单位),如果超过了,子进程将被 kill 掉(发送 killSignal 值),它的默认值是200kb,这也就很大程度上决定了它的实用场景,即在子进程长时间运行或者大量的日志输出时,需要谨慎设置maxBuffer的值。这种情况下使用spawn方法更为合适。
spawn
spawn(command, args, config)
spawn方式适合用在进程的输入、输出数据量比较大的情况可以用于任何命令。但是它与exec方法比较起来就是在执行多条命令的时候使用起来可能不太方便。如果我们要执行多条命令,可以通过新建shell脚本的方式来实现。
import { spawn } from 'child_process'
const child = spawn('sh', [path.join(__dirname, './test.sh'), '192.100.100.100'])
然后在shell脚本里面执行下面的命令
ssh suoper@$1
<< eeooff
cd ${apiConfig.apkUpload.path}
exit
eeooff
fork
fork(filePath, args, options)
fork命令只能执行用来执行node脚本,同事会创建一个新的V8实例。
execFile
execFile(file, args, options, callback)
需要注意的是,exec 会首先创建一个新的 shell 进程出来,然后执行 command。execFile 则是直接将可执行的 file 创建为新进程执行。所以,execfile 会比 exec 高效一些。exec 比较适合用来执行 shell 命令,然后获取输出(比如:exec('ps aux | grep "node"')),但是 execFile 却没办法这么用,因为它实际上只接受一个可执行的命令,然后执行(没法使用 shell 里面的管道之类的东西)。