大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
子进程
Node 主进程是 单线程 Event Loop。nodejs-advanced-essentials 的 CLI 通常单进程即可;当需要 跑 shell、隔离崩溃、复用多核(配合 cluster) 时用 node:child_process。
学习目标
- 选择
spawn、exec、execFile、fork的适用场景 - 处理 stdin/stdout/stderr 与退出码
- 理解
fork与 IPC 消息通道
四种启动方式
| 方法 | 特点 | 典型场景 |
|---|---|---|
spawn(cmd, args) | 流式 I/O,不缓冲全量输出 | 长跑命令、管道 |
exec(cmd) | 缓冲 stdout/stderr,有 maxBuffer 上限 | 短 shell 一行命令 |
execFile(file, args) | 不经过 shell,更安全 | 调用固定可执行文件 |
fork(modulePath) | Node 专用,自带 IPC | 子 Node 进程、cluster 基础 |
import { spawn } from "node:child_process";
const child = spawn("node", ["--version"], { stdio: "inherit" });
child.on("close", (code) => {
console.log("exit code:", code);
});
安全:避免 shell 注入
反模式:exec(git clone ${userInput})——用户输入可注入 ; rm -rf /。
推荐:spawn("git", ["clone", url], { shell: false }) 或 execFile 传数组参数。
与 nva-cli 解析 argv 同理:永远不要把未校验字符串拼进 shell。
fork 与 IPC
fork 启动子 Node 进程,并通过 send / message 传 JSON 可序列化对象:
import { fork } from "node:child_process";
if (process.send) {
// 子进程
process.on("message", (msg) => {
process.send({ ok: true, echo: msg });
});
} else {
const child = fork(new URL("./worker-child.js", import.meta.url));
child.on("message", (m) => console.log("from child:", m));
child.send({ task: "ping" });
}
大数据应用 流或 SharedArrayBuffer(见 nrt-worker),不要频繁传 MB 级对象。
退出码与错误
child.on("error", (err) => {
// 无法 spawn(命令不存在等)
});
child.on("exit", (code, signal) => {
if (code !== 0) console.error("failed", code, signal);
});
CI 脚本应 process.exitCode = child.exitCode ?? 1,与 node-runtime 一致。
与 Worker Threads 选型
| child_process | worker_threads | |
|---|---|---|
| 内存 | 独立 V8 实例 | 同进程,可共享内存 |
| 启动成本 | 较高 | 较低 |
| 适用 | shell、非 Node 程序、强隔离 | CPU 密集、同进程通信 |
练习
- 用
spawn跑ls(Windows 用dir),把 stdout pipe 到父进程 console - 写
fork父子互发{ n: 1 },子进程计算n * 2回传 - 对比
exec超大输出触发 maxBuffer 错误的复现
小结
spawn 流式、exec 短命令、fork 带 IPC 的子 Node。 下一课 Worker Threads——同进程内多线程跑 CPU 任务。