操作系统进程与 Node.js 进程

本章解决的问题

工作原理

进程是操作系统分配资源和隔离程序的一种单位,通常拥有 PID、虚拟地址空间、打开的文件描述符和权限。线程是进程内部的执行单位,同一进程中的线程可访问同一地址空间;不同进程默认不能直接访问彼此内存。

Node.js 执行普通 JavaScript 时,主要由一个 JavaScript 主线程运行 Event Loop。但一个 Node.js 进程还可能包含:

因此,“JavaScript 默认在一个主线程执行”比“Node.js 只有一个线程”准确。

Node.js 进程 PID 100
├─ JavaScript 主线程:回调、Promise、Event Loop
├─ libuv 线程池:部分阻塞型底层工作
└─ 系统异步 I/O

另一个 Node.js 进程 PID 101
└─ 拥有独立 V8、Event Loop、堆和模块缓存

高频 API

console.log({
  pid: process.pid,
  parentPid: process.ppid,
  platform: process.platform,
  architecture: process.arch,
});

具体代码:验证内存不共享

保存为 counter.ts

let counter: number = 0;

counter += 1;

console.log({
  pid: process.pid,
  counter,
});

分别执行两次:

ts-node counter.ts
ts-node counter.ts

两个进程都会输出 counter: 1,而不是第二次输出 2。模块级变量只属于各自进程。

服务中也一样:若 4 个 Cluster Worker 各有一个 Map<string, Task>,实际是 4 张互不相同的表。共享任务状态应放入数据库、Redis 等外部系统。

正常时间线

Shell 创建 Node.js 进程
→ 操作系统分配 PID 和地址空间
→ Node.js 初始化 V8/libuv
→ 执行入口模块
→ Event Loop 无待处理工作
→ 进程退出并向父进程报告退出状态

异常时间线

入口模块抛出未捕获异常
→ Node.js 打印诊断到 stderr
→ 进程以非零状态退出
→ 该进程内存和连接全部消失
→ 其他隔离进程通常仍可运行

进程隔离可以限制一次崩溃的范围,但不会自动恢复任务、释放外部锁或保证数据一致性。

Windows 与 Linux

常见错误

  1. 把异步 I/O 当成多线程执行 JavaScript 回调:回调最终仍回到主线程排队执行。
  2. 用 Cluster 解决 CPU 密集型请求,却没有控制进程数和内存:每个进程都带独立 V8 堆。
  3. 认为 process.ppid 永远有效:父进程可能先退出,关系也可能被系统重新托管。
  4. 用进程内变量实现多进程全局限流:每个 Worker 都会独立计数。

最佳实践

练习题

  1. 连续运行 counter.ts 三次,记录 PID 与 PPID。
  2. 在脚本中创建一个大数组,观察每个独立进程是否各自占用内存。
  3. 判断以下对象能否直接传给另一个进程:函数、JSON 对象、数据库连接、文件路径。

验收点