查看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
ps -ef | grep 9668
ps -ef | grep 9665
ps -ef | grep 9620
ps -ef | grep 9617
ps -ef | grep 9601 vscode开了三个进程运行这条命令,吐血
ps -ef 终于找到了/sbin/launchd这个是桌面程序的进程
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 是一次性输出结果 */
const npmFilePath = "/Users/rainbow/Documents/前端/脚手架开发/rainbow-cli-dev";
cp.exec(
"npm install",
{
cwd: npmFilePath,
},
(error, stdout, stderr) => {
console.log(error, stdout, stderr);
}
);
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);
})
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
exec使用的是execFile方法,只是参数不一样
function exec(command, options, callback) {
const opts = normalizeExecArgs(command, options, callback);
return module.exports.execFile(opts.file,
opts.options,
opts.callback);
}
- 查看创建的子进程源码: