node子进程

461 阅读2分钟

查看node进程: ps -ef | grep node

  • 可以查看node启动的进程

如何查看我们自己启动的node进程

  • 进入debug模式可以查看到(本次使用vscode进行debug)
  • 找到进程ID
  • ps -ef | grep 进程ID
  • 找打进程ID对应的父进程ID
  • ps -ef | grep 父进程ID
  • 继续查找父进程ID的父进程ID
    • sbin/launch 桌面进程

ps -ef | grep node image.png

ps -ef | grep 9668

image.png

ps -ef | grep 9665

image.png

ps -ef | grep 9620

image.png

ps -ef | grep 9617 image.png

ps -ef | grep 9601 vscode开了三个进程运行这条命令,吐血 image.png

ps -ef 终于找到了/sbin/launchd这个是桌面程序的进程 image.png

iShot2021-11-28 21.28.25.png

Node.js中的子进程

使用node的内置库:child_process

const cp = require("child_process");
const path = require("path");

cp.exec 用子进程执行命令

// cp.exec用的就是cp.execFile,只是不支持['-al']
cp.exec(
  path.resolve(__dirname, "test.shell"),
  function (err, stdout, stdError) {
    console.log("exec", stdout);
  }
);

cp.exec(
  path.resolve(__dirname, "test.shell"),
  {
timeout: 1,
    cwd: process.cwd(".."),
  }, //超时执行命令会终止执行
  function (err, stdout, stdError) {
    console.log("exec1", stdout);
  }
);

cp.execFile用子进程执行shell文件或者命令

cp.execFile(
  path.resolve(__dirname, "test.shell"),
  ["-al"],
  (error, stdout, stderr) => {
    console.log(error);
    console.log(stdout);
  }
);

cp.spawn返回一个子进程

使用场景: spawn: 耗时任务(比如: npm install),需要不断日志 exec/execFile: 开销比较小的任务

const child = cp.spawn(path.resolve(__dirname, "test.shell"), ["-ls"]);
// // 打印输出信息
child.stdout.on("data", (data) => {
  console.log(`stdout: ${data}`);
});
// 打印error信息,正确的信息任然会被执行
child.stderr.on("data", (data) => {
  console.log(`stderr: ${data}`);
});

测试npm i两种的场景

/**spawn 是不断输出结果 */
const npmFilePath = "${path}/rainbow-cli-dev";
const child = cp.spawn("cnpm", ["install"], {
  cwd: npmFilePath,
});
child.stdout.on("data", (chunk) => {
  console.log(`stdout::: ${chunk.toString()}`);
});
child.stderr.on("data", (chunk) => {
  console.log(`stderr::: ${chunk.toString()}`);
});

spawn场景下的npminstall是断断续续输出结果.png

/**spawn 是一次性输出结果 */
const npmFilePath = "/Users/rainbow/Documents/前端/脚手架开发/rainbow-cli-dev";
cp.exec(
  "npm install",
  {
    cwd: npmFilePath,
  },
  (error, stdout, stderr) => {
    console.log(error, stdout, stderr);
  }
);

iShot2021-12-01 19.08.50.png

iShot2021-12-01 19.08.58.png

stdio: "inherit"

将子进程的输出流入父进程,npm i可以查看到下载过程

const npmFilePath = "${path}/rainbow-cli-dev";
const child = cp.spawn("npm", ["install"], {
  cwd: npmFilePath,
  stdio: "inherit",
});
child.on('error',(e)=>{
    console.error(e.message);
    process.exit(1);
})
child.on('exit',(e)=>{
    console.log("命令执行成功:" + e);
    process.exit(e);
})

iShot2021-12-01 19.22.29.png

iShot2021-12-01 19.22.35.png

fork 创建一个子进程,Node(main)-Node(child)

  • child.js文件像require文件一样,读取文件并解析了
  • 功能1:但是不同于require的是,require使用的同一个node主进程,但是fork新增了一个子进程来读取和解析文件
  • 功能2:实现主进程与子进程之间的通信
  • 使用场景:耗时操作
/**
 * main 6344
 * child_process 6345
 */
const child = cp.fork(path.resolve(__dirname, "child.js")); // 异步的
console.log("main", process.pid);
child.send("hello child process!", () => {
  //给子进程发送消息,并且处于等待中
  // child.disconnect();
});
child.on("message", (msg) => {
  console.log(msg);
  child.disconnect();
});
/**
执行顺序
main 6518
child_process 6519
hello main
hello child process!
 */
//child.js
console.log("child_process", process.pid);
process.on("message", (msg) => {
  console.log(msg);
});
process.send("hello main");

child_process 同步方法

const ret = cp.execSync("ls -al| grep node_modules");
console.log(ret.toString());

const ret1 = cp.execFileSync("ls", ["-al"]);
console.log(ret1.toString());

const ret2 = cp.spawnSync("ls", ["-al"]);
console.log("ret2", ret2.output.toString());

node.js内置库child_process源码解析

  • exec和execFile到底什么区别
  • 为什么exec/execFile/fork都是通过spawn实现的?,spawn的作用到底是什么?
  • 为什么spawn调用后没有回调,而是exec和execFile能够回调?
  • 为汉森么spawn调用后需要手动调用child.stdout.on('data',callback),这里的child.stdout / child.stderr到底是什么?
  • 为什么有data/error/exit/close这么多种回调,他们的执行顺序是怎样的?

调试node.js内置库child_process

调试nodejs元源码方法

iShot2021-12-02 11.25.10.png

exec使用的是execFile方法,只是参数不一样

function exec(command, options, callback) {
  const opts = normalizeExecArgs(command, options, callback);
  return module.exports.execFile(opts.file,
                                 opts.options,
                                 opts.callback);
}
  • 查看创建的子进程源码:

iShot2021-12-02 14.49.02.png