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.
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?
kill ()
the process of ko.js.Such a situation will occur.
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.
Then, how can I make the grandchild process terminate when the started child process is kill ()
? I would like to explain that here.
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.
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)
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
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