The preface
This article is mainly to record their own learning plan, will be from the module child_process spawn/ fork/ exec/ execFile four aspects to explain the node to create child process to execute
Pay attention to
If you don’t know much about Node and child_process, it’s best to read the official documentation for this article
The address of the example code below
Example code address reference
spawn
Default output sharing
// parent.js const { spawn } = require('child_process') const cp = spawn('node', ['son.js'], { cwd: __dirname, stdio: [process.stdin, process.stdout, process.stderr]}) () = > {the console. The log (' the child process closed ')}) / / to monitor the child closed cp. On (' exit '() = > {the console. The log (' the child out of the')}) / / son, js let sum = 0, i = 0 for (; i < 100 * 2000 * 4000; i++) sum += i console.log(sum)Copy the code
- Here is an overview of the above important parameters:
- CWD indicates the working directory for running the current command
- Spwan {stdio} is [0, 1, 2], indicating that the input and output of the subclass are shared with the parent class. How to understand data sharing?? Console. log is used by the parent class, so it is printed to the control side of the parent class. If you don’t believe me, change the stdio parameter to [0, null, 2]. So it won’t print. Because NULL means no sharing
- What if you don’t really want to output. I want the subroutine to tell me it’s done, so keep reading!!
Implementation based on PIE
// parent.js
const { spawn } = require('child_process')
const cp = spawn('node'['son.js'] and {cwd: __dirname,
// Communication between processes is stream-based
stdio: ['pipe'.'pipe'.'pipe']})// Subscribe subclass to parent class based on data passed by stream
cp.stdout.on('data'.(e) = > {
console.log(e.toString())
})
// Subscribe error message
cp.on('error'.(e) = > {
console.log('error: ', e.toString())
})
// Write data to subclasses based on the stream
cp.stdin.write('Parent gives subclass data')
// The subscription process is closed
cp.on('close'.() = > {
console.log('Child process closed')})// The subscription process exits
cp.on('exit'.() = > {
console.log('Child process exits')})// son.js
let sum = 0,
i = 0
for (; i < 100 * 2020 * 2300; i++) sum += i
process.stdout.write(String(sum))
// Subscribes data from parent to subclass
process.stdin.on('data'.(e) = > {
console.log(e.toString())
process.nextTick(() = > {
process.kill()
})
})
Copy the code
- This is the result of the above code execution, the communication between the parent and child is stream-based, indicating that the bottom layer is stream-based to achieve
- When a subclass notifies its parent class of a message, it can write it, and the parent class can listen for changes in the stream to get data
fork
It can be understood that fork is actually an advanced version of SPwan ipc communication, and fork is based on IPC communication to achieve, the subsequent analysis of the source code will be analyzed
Instance analysis
// parent.js
const { fork } = require('child_process')
const cp = fork('son.js', {
cwd: __dirname
})
cp.on('message'.(e) = > {
console.log(e)
})
cp.send("Hello, subclass.".function () {})
cp.on('close'.function () {
console.log('Child process closed')
})
cp.on('exit'.function () {
console.log('Child process exits')})// son.js
let sum = 0,
i = 0
for (; i < 100 * 3000 * 300; i++) sum += i
process.send('The result is:' + sum)
process.on('message'.function (e) {
console.log(e)
process.exit()
})
Copy the code
- Above is the result of the above code:
- Fork is an advanced version of Spwan
- Fork is designed by nature to communicate over an IPC channel, with the input/output standard being [0, 1, 2, ‘IPC ‘]
- The send function is used to communicate messages and listen for messages
- A channel cannot be closed automatically once communication has started because a channel has already been established
- Fork is performed using Node by default
The source code parsing
The following source code has been deleted from the node source child_process.js
function fork(modulePath /* , args, options */) {
// Check if it is a valid string
validateString(modulePath, 'modulePath');
// Get options and args arguments.
let execArgv;
let options = {};
let args = [];
let pos = 1;
if (pos < arguments.length && arguments[pos] ! =null) {
if (typeof arguments[pos] ! = ='object') {
throw new ERR_INVALID_ARG_VALUE(`arguments[${pos}] `.arguments[pos]);
}
// Merge parameters CWD and STDIOoptions = { ... arguments[pos++] }; }// Prepare arguments for fork:
execArgv = options.execArgv || process.execArgv;
args = execArgv.concat([modulePath], args);
if (typeof options.stdio === 'string') {
options.stdio = stdioStringToArray(options.stdio, 'ipc');
// This process is performed if stdio is not set when forking
} else if(! ArrayIsArray(options.stdio)) {// Use a separate fd=3 for the IPC channel. Inherit stdin, stdout,
// and stderr from the parent if silent isn't set.
// The result of the last conversion is [0, 1, 2, 'ipc']
options.stdio = stdioStringToArray(
options.silent ? 'pipe' : 'inherit'.'ipc');
} else if(! options.stdio.includes('ipc')) {
throw new ERR_CHILD_PROCESS_IPC_REQUIRED('options.stdio');
}
// Set the node address
options.execPath = options.execPath || process.execPath;
// Set the execution shell to false
options.shell = false;
// Fork is used to communicate between processes based on spawn
return spawn(options.execPath, args, options);
}
Copy the code
- This is the source code for fork, which is actually based on spawn, but the default channel is IPC, and it must be IPC
execFile
Instance analysis
// parent.js
const { execFile } = require('child_process')
// Execute the file directly
const cp = execFile('node'['son.js'] and {cwd: __dirname
}, function(err, stdout, stderr) {
// Trigger the result with a callback
console.log(stdout)
})
cp.on('close'.function() {
console.log('Child process closed')
})
cp.on('exit'.function() {
console.log('Child process exits')})// son.js
let sum = 0, i = 0
for (; i < 100 * 300 * 400; i ++) sum += i
console.log(sum)
Copy the code
- The screenshot above is the result of an example execution:
- The above code execution process is also spawn based
- The default for execFile execution is shell: false
- The stdio parameter is the default [0, 1, 2]
The source code parsing
function execFile(file /* , args, options, callback */) {
let args = [];
let callback;
let options;
// Parse the optional positional parameters.
let pos = 1;
if (pos < arguments.length && ArrayIsArray(arguments[pos])) {
// File to execute or command to execute
args = arguments[pos++];
} else if (pos < arguments.length && arguments[pos] == null) {
pos++;
}
if (pos < arguments.length && typeof arguments[pos] === 'object') {
options = arguments[pos++];
} else if (pos < arguments.length && arguments[pos] == null) {
pos++;
}
if (pos < arguments.length && typeof arguments[pos] === 'function') {
// To get the callback function
callback = arguments[pos++];
}
if(! callback && pos <arguments.length && arguments[pos] ! =null) {
throw new ERR_INVALID_ARG_VALUE('args'.arguments[pos]);
}
options = {
encoding: 'utf8'.timeout: 0.maxBuffer: MAX_BUFFER,
killSignal: 'SIGTERM'.cwd: null.env: null.shell: false. options };ExecFile is implemented based on spawn
const child = spawn(file, args, {
cwd: options.cwd,
env: options.env,
gid: options.gid,
shell: options.shell,
signal: options.signal,
uid: options.uid,
windowsHide:!!!!! options.windowsHide,windowsVerbatimArguments:!!!!! options.windowsVerbatimArguments });Copy the code
exec
const { exec } = require('child_process')
exec('path', {cwd: __dirname}, function(err, stdout, stderr) {
console.log(stdout)
console.log(stderr.toString())
})
Copy the code
- The screenshot above is the result of the example:
- First, the exec command can be executed using a string execution write command
- The shell attribute in the exec command defaults to true
- Exec is implemented based on execFile
The source code parsing
function exec(command, options, callback) {
// Set the default shell to true in the function normalizeExecArgs
const opts = normalizeExecArgs(command, options, callback);
return module.exports.execFile(opts.file,
opts.options,
opts.callback);
}
Copy the code
Look, the implementation principle of exec function is very simple
Conclusion:
- As you can see from the above examples, all but spawn are implemented more or less based on spawn
- There are several ways for parent processes to communicate in Spawn, in addition to the default (process.stdin/ stdout), the flow-based PIE, and the IPC based message channel
- The child process creation of fork is itself Node-based
- The exec function and execFile differ in shell parameters
At the end
Well, that’s about it. So much for the usual. Let’s introduce ourselves
- GitHub
- Personal blog
- Exhibition collection of personal works