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