> For the complete documentation index, see [llms.txt](https://1425816423.gitbook.io/my-knowledge-base/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://1425816423.gitbook.io/my-knowledge-base/fu-wu-qi-duan/nodejs/node-zi-jin-cheng.md).

# Node子进程

tags: Nodejs Created time: July 21, 2022 8:10 PM emoji: <https://nodejs.org/static/images/favicons/favicon.png>

Nodejs有四种方式创建子进程

* `spawn`
* `exec`
* `fork`
* `execFile`

## spawn

spawn使用示例

```jsx
const childProcess = require("child_process")

var spawn = childProcess.spawn('ipconfig')
var spawn = childProcess.spawn('ipconfig'， [a])  // 参数
var spawn = childProcess.spawn('ipconfig'， [a]， {})  // 配置
```

spawn的第二个参数是一个数组，用于传递子进程命令参数。

第三个参数是一个配置对象，里面比较常用的属性有：

* cwd： 子进程当前工作目录
* shell： 是否在shell中执行命令，默认是false

**如果启用了 `shell` 选项，请不要将未经清理的用户输入传递给此函数。任何包含 shell 字符的输入都可用于触发任意命令执行。**

spawn函数的返回值有三个流，由这三个流来获取子进程的输入输出和异常

* stdout
* stdin
* stderr

```jsx
spawn.stdout.pipe(process.stdout)
spawn.stderr.pipe(process.stdout)
spawn.stdin.pipe(process.stdin)
// 也可以使用事件
spawn.stdout.on('data'(data)=>{
	console.log(data)
})
```

## exec

exec的执行方式和spawn不同

```jsx
const exec = childProcess.exec("node a.js a",(err,stdout,stderr)=>{
    console.log(stdout);
})
// 可以接收一个配置对象
const exec = childProcess.exec("node a.js a",{
    "cwd": "./"
},(err,stdout,stderr)=>{
    console.log(stdout);
})
```

区别有

* **默认开启shell**
* 参数写在code(第一个参数)里，用空格分隔
* 输入输出流在回调中

> **切勿将未经清理的用户输入传递给此函数。任何包含 shell 元字符的输入都可用于触发任意命令执行。**

exec的回调第一个参数是异常对象，第二个是输出流，第三个是异常流。

exec可以接收一个配置对象，常用的属性有：

* encoding ：默认'utf8’
* shell： 默认开启
* cwd： 当前工作目录

## execFile

该函数类似于`[child_process.exec（）](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback)`只是默认情况下它不会生成 shell。相反，指定的可执行文件直接作为新进程生成，使其比`[child_process.exec（）](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback)`稍微高效一些。

execFile可以执行可执行文件，第一个参数传入要运行的可执行文件的名称或路径。

```jsx
childProcess.execFile("node ./a.js",{
    shell: true
},(err,stdout,stderr)=>{
    if(err){ 
        console.error(err);
        return err;
    }

    console.log(stdout);
})
```

## fork

该方法是`[child_process.spawn（）](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)`的特殊情况，专门用于生成新的Node.js进程。与`[child_process.spawn（）](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)` 一样，返回一个 `[ChildProcess](https://nodejs.org/api/child_process.html#class-childprocess)`对象。**返回的`[子进程](https://nodejs.org/api/child_process.html#class-childprocess)`将具有一个内置的附加通信通道，该通道允许在父级和子级之间来回传递消息。**

请记住，生成的 Node.js子进程独立于父进程，但在两者之间建立的 IPC 通信通道除外。每个进程都有自己的内存，有自己的 V8 实例。由于需要额外的资源分配，因此不建议生成大量子Nodejs进程。

```jsx
if (process.argv[2] === "child") {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`);
  }, 1_000);
  process.send('hello 123')
} else {
  const { fork } = require("node:child_process");
  const controller = new AbortController();
  const { signal } = controller;
  const child = fork(__filename, ["child"], { signal });
  child.on("message", (data) => { 
    console.log("收到的消息：" + data);
  });
	//   controller.abort(); // 停止子进程
}
```

fork生成的子进程与父进程之间会建立好一条通信通道，可以通过`process.send`发送信息，另一方通过监听`message`事件获取。

## 如何停止子进程

上诉的几种创建子进程的方式的配置对象中都有一个`signal` 属性，用于接收一个Abort控制器，可以这种方式停止子进程。

```jsx
const { fork } = require("node:child_process");
const controller = new AbortController();
const { signal } = controller;
const child = fork(__filename, ["child"], { signal });
child.on("message", (data) => { 
  console.log("收到的消息：" + data);
});
controller.abort(); // 停止子进程
```
