[LINUX] Node.js: How to kill offspring of a process started with child_process.fork ()

In this article, I will explain how to terminate child processes, grandchild processes derived from them, and great-grandchild processes derived from them all at once in Node.js.

Killing a child process does not kill the grandchild process

Node.js [child_process.fork ()] is convenient because it can start a child process. You can also use fork () in a child process to start a grandchild process, and thenfork ()in a grandchild process to make a great-grandchild process, and so on. You can start it.

The started child process can be killed with [subprocess.kill ()]. However, this can only kill the direct child process. What does that mean?

  1. oya.js launches the ko.js process.
  2. ko.js launches the mago.js process.
  3. At this time, suppose that oya.js kill () the process of ko.js.
  4. Ko.js is closed.
  5. mago.js survives. (* At this time, mago.js is adopted by the init process, and the parent pid becomes 1.)

Such a situation will occur.

Sample code with grandchild processes remaining

I would like to write code that can reproduce the above scenario.

First, the implementation of oya.js:

oya.js


console.log('oya.js: running')

//When SIGINT is accepted
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

//When the process ends
process.on('exit', () => {
  console.log('oya.js: exit')
})

//Start child process
const ko = require('child_process')
  .fork(__dirname + '/ko.js')

//Proc2 after 3 seconds.Exit js
setTimeout(() => {
  console.log('oya.js: ko.I'm terminating js...')
  ko.kill('SIGINT')
}, 3000)

// ko.When js finishes
ko.on('exit', () => {
  console.log('> Ctrl-Press C...')
})

//The magic to keep this process running forever
setInterval(() => null, 10000)

oya.js is the code that starts ko.js and terminates ko.js after 3 seconds. When I kill () ko.js, I try to send a SIGINT signal. Linux signals are not discussed in detail here. Think of the SIGINT signal here as simply telling you to end the process.

Next, ko.js:

ko.js


console.log('ko.js: running')

//When SIGINT is accepted
process.on('SIGINT', () => {
  console.log('ko.js: SIGINT')
  process.exit()
})

//When the process ends
process.on('exit', () => {
  console.log('ko.js: exit')
})

//Start grandchild process
require('child_process')
  .fork(__dirname + '/mago.js')

//The magic to keep this process running forever
setInterval(() => null, 10000)

Finally, mago.js:

mago.js


console.log('mago.js: running')

//When SIGINT is accepted
process.on('SIGINT', () => {
  console.log('mago.js: SIGINT')
  process.exit()
})

//When the process ends
process.on('exit', () => {
  console.log('mago.js: exit')
})

//The magic to keep this process running forever
setInterval(() => null, 10000)

Let's run this code:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.I'm terminating js...
ko.js: SIGINT
ko.js: exit
> Ctrl-Press C...

After 3 seconds, you can see that oya.js kills () ko.js and ko.js is finished.

On the other hand, mago.js hasn't received SIGINT yet, hasn't finished, and remains.

Now press Ctrl-C to send SIGINT to oya.js and mago.js:

...
> Ctrl-Press C...
^Coya.js: SIGINT
mago.js: SIGINT
mago.js: exit
oya.js: exit

Only at this timing will you know that mago.js will end.

In my opinion, this result was surprising because I misunderstood that if I sent SIGINT to ko.js, SIGINT would be propagated to mago.js as well.

How to kill offspring of a started process

Then, how can I make the grandchild process terminate when the started child process is kill ()? I would like to explain that here.

Process group = "household"

First of all, there is a process group as the basis of Linux processes. It's a "household" -like concept of a process that groups parent, child, and grandchild processes. For example, if you start the node process oya.js in Bash, ko.js and mago.js fork () from there belong to the same process group and are given the same group ID.

If you check the group ID (GPID) with the ps command, you can see that the same group ID is actually assigned to the three node processes:

$ ps -xo pid,ppid,pgid,command | grep node | grep .js
PID   PPID  GPID  COMMAND
17553  3528 17553 node oya.js
17554 17553 17553 node ko.js
17555 17554 17553 node mago.js

As you can see from this result, the GPID is the same as the process ID (PID) of oya.js. In other words, the parent's PID becomes the offspring's GPID.

How to kill an entire "household" process

In Node.js, you can specify the group ID to terminate the process. All you have to do is pass the GPID to [process.kill ()]. At this time, give a negative number. Note that passing a positive number will only kill () individual processes, not process groups.

const groupId = 123456
process.kill(-groupId, 'SIGINT')

By the way, when Ctrl-C is pressed in the shell, the parent, child, and grandchild are all terminated because the SIGINT sent by Ctrl-C is sent to the process group, not to the parent process. Because it has been done. (Citation needed)

detached = create another household

What I want to do this time is to kill () ko.js and mago.js while keeping the oya.js process alive. However, with kill () with GPID specified, oya.js will be terminated. Because all three have the same GPID:

PID   PPID  GPID  COMMAND
17553  3528 17553 node oya.js
17554 17553 17553 node ko.js
17555 17554 17553 node mago.js

You need to assign different GPIDs for ko.js and mago.js. To do that, specify detached as an option forfork ().

oya.js


//Start child process
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true})

If this is specified, ko.js and mago.js will be, so to speak, "separate households" and will belong to different process groups. You can see that the GPID is also assigned differently from oya.js:

$ ps -xo pid,ppid,pgid,command | grep node | grep .js
PID   PPID  GPID  COMMAND
21404  3528 21404 node oya.js
21405 21404 21405 node ko.js
21406 21405 21405 node mago.js

Completed form of oya.js that kills offspring of the process

Based on the above, if you change oya.js so that child processes and grandchild processes can be terminated at once, it will be as follows:

oya.js


console.log('oya.js: running')

//When SIGINT is accepted
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

//When the process ends
process.on('exit', () => {
  console.log('oya.js: exit')
})

//Start child process
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true}) //Important changes!

//Ko after 3 seconds.Exit js
setTimeout(() => {
  console.log('oya.js: ko.I'm terminating js...')
  process.kill(-ko.pid, 'SIGINT') //Important changes!
}, 30000)

// ko.When js finishes
ko.on('exit', () => {
  console.log('> Ctrl-Press C...')
})

//The magic to keep this process running forever
setInterval(() => null, 10000)

Finally, let's run this oya.js and see if ko.js and mago.js are finished together:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.I'm terminating js...
mago.js: SIGINT
ko.js: SIGINT
mago.js: exit
ko.js: exit
> Ctrl-Press C...
^Coya.js: SIGINT
oya.js: exit

As expected, ko.js and mago.js have received SIGINT at the same time and finished. You can also see that oya.js is alive until you press Ctrl-C.

So far, we have explained how to kill offspring of a process started by child_process.fork () of Node.js.

Recommended Posts

Node.js: How to kill offspring of a process started with child_process.fork ()
How to kill a process instantly with Python's Process Pool Executor
Here's a brief summary of how to get started with Django
How to display a list of installable versions with pyenv
How to get started with Scrapy
How to get started with Python
How to get started with Django
How to insert a specific process at the start and end of spider with scrapy
How to add a package with PyCharm
How to get started with laravel (Linux)
[EC2] How to take a screen capture of your smartphone with selenium
[Introduction to Python] How to sort the contents of a list efficiently with list sort
How to study until a beginner in statistics gets started with Bayesian statistics
Summary of how to build a LAMP + Wordpress environment with Sakura VPS
How to calculate the volatility of a brand
How to read a CSV file with Python 2/3
A simple example of how to use ArgumentParser
How to send a message to LINE with curl
How to draw a 2-axis graph with pyplot
How to develop a cart app with Django
How to make a dictionary with a hierarchical structure.
How to specify attributes with Mock of python
How to create a multi-platform app with kivy
A layman wants to get started with Python
How to apply updlock, rowlock, etc. with a combination of SQLAlchemy and SQLServer
[Python] How to easily drop a child process started by multiprocess from another process
How to get a list of files in the same directory with python
[Introduction to Python] How to get the index of data with a for statement
A new form of app that works with GitHub: How to make GitHub Apps
How to process camera images with Teams or Zoom
How to convert / restore a string with [] in python
A memo of how to use AIST supercomputer ABCI
[Python] How to draw a line graph with Matplotlib
How to create a submenu with the [Blender] plugin
How to identify the element with the smallest number of characters in a Python list?
How to get a logged-in user with Django's forms.py
How to convert a class object to a dictionary with SQLAlchemy
How to make a shooting game with toio (Part 1)
How to write a list / dictionary type of Python3
How to get started with Visual Studio Online ~ The end of the environment construction era ~
How to output CSV of multi-line header with pandas
How to infer MAP estimate of HMM with PyStruct
Basics of PyTorch (2) -How to make a neural network-
How to infer MAP estimate of HMM with OpenGM
A memo on how to overcome the difficult problem of capturing FX with AI
[Python] How to create a 2D histogram with Matplotlib
How to learn structured SVM of ChainCRF with PyStruct
[Python] How to draw a scatter plot with Matplotlib
Summary of how to share state with multiple functions
Run the program without building a Python environment! !! (How to get started with Google Colaboratory)
How to process camera images with Teams and Zoom Volume of processing in animation style
How to plot a lot of legends by changing the color of the graph continuously with matplotlib
How to deploy a web app made with Flask to Heroku
How to make a Cisco Webex Teams BOT with Flask
[Ubuntu] How to delete the entire contents of a directory
How to put a hyperlink to "file: // hogehoge" with sphinx-> pdf
How to install NPI + send a message to line with python
[Python] How to make a list of character strings character by character
How to convert an array to a dictionary with Python [Application]
How to output a document in pdf format with Sphinx
A collection of competitive pro techniques to solve with Python