[LINUX] Node.js: Comment tuer les descendants d'un processus démarré par child_process.fork ()

Dans cet article, nous expliquerons comment terminer les processus enfants, les processus petits-enfants qui en découlent et les processus d'arrière-petits-enfants qui en dérivent tous en même temps dans Node.js.

Tuer un processus enfant ne tue pas le processus petit-enfant

Node.js [child_process.fork ()] est pratique car il peut démarrer des processus enfants. Dans un processus enfant, vous pouvez utiliser fork () pour démarrer un processus petit-enfant, et dans un processus petit-enfant, fork () pour créer un processus arrière-petit-enfant, et ainsi de suite. Vous pouvez le démarrer.

Le processus enfant démarré peut être arrêté avec [subprocess.kill ()]. Cependant, cela ne peut tuer que les processus enfants directs. Qu'est-ce que ça veut dire?

  1. oya.js lance le processus ko.js.
  2. ko.js lance le processus mago.js.
  3. À ce stade, supposons que oya.js kill () le processus de ko.js.
  4. ko.js est fermé.
  5. mago.js survit. (* À ce stade, mago.js est adopté par le processus init, et le pid parent devient 1.)

Une telle situation se produira.

Exemple de code avec processus de petit-enfant résiduel

Je voudrais écrire du code qui puisse reproduire le scénario ci-dessus.

Tout d'abord, la mise en œuvre de oya.js:

oya.js


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

//Lorsque SIGINT est accepté
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

//Quand le processus se termine
process.on('exit', () => {
  console.log('oya.js: exit')
})

//Démarrer le processus enfant
const ko = require('child_process')
  .fork(__dirname + '/ko.js')

//Proc2 après 3 secondes.Quitter js
setTimeout(() => {
  console.log('oya.js: ko.Je termine js...')
  ko.kill('SIGINT')
}, 3000)

// ko.Quand js a fini
ko.on('exit', () => {
  console.log('> Ctrl-Appuyez sur C...')
})

//La magie pour que ce processus continue de fonctionner pour toujours
setInterval(() => null, 10000)

oya.js est le code qui démarre ko.js et termine ko.js après 3 secondes. Quand je kill () ko.js, j'essaye d'envoyer un signal SIGINT. Les signaux Linux ne sont pas décrits en détail ici. Pensez ici au signal «SIGINT» comme indiquant simplement la fin du processus.

Ensuite, ko.js:

ko.js


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

//Lorsque SIGINT est accepté
process.on('SIGINT', () => {
  console.log('ko.js: SIGINT')
  process.exit()
})

//Quand le processus se termine
process.on('exit', () => {
  console.log('ko.js: exit')
})

//Démarrer le processus des petits-enfants
require('child_process')
  .fork(__dirname + '/mago.js')

//La magie pour que ce processus continue de fonctionner pour toujours
setInterval(() => null, 10000)

Enfin, mago.js:

mago.js


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

//Lorsque SIGINT est accepté
process.on('SIGINT', () => {
  console.log('mago.js: SIGINT')
  process.exit()
})

//Quand le processus se termine
process.on('exit', () => {
  console.log('mago.js: exit')
})

//La magie pour que ce processus continue de fonctionner pour toujours
setInterval(() => null, 10000)

Exécutons ce code:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.Je termine js...
ko.js: SIGINT
ko.js: exit
> Ctrl-Appuyez sur C...

Après 3 secondes, vous pouvez voir que cette sortie est sortie, oya.js kill () ko.js, et ko.js est terminé.

D'un autre côté, mago.js n'a pas encore reçu SIGINT, n'a pas fini et reste.

Maintenant, appuyez sur Ctrl-C pour envoyer SIGINT à oya.js et mago.js:

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

Ce n'est qu'à ce moment que vous saurez que mago.js se terminera.

À mon avis, ce résultat était surprenant car je ne comprenais pas que si j'envoyais SIGINT à ko.js, SIGINT serait également propagé vers mago.js.

Comment tuer un processus engendré

Alors, comment puis-je faire terminer le processus petit-enfant lorsque le processus enfant démarré est kill ()? Je voudrais expliquer cela ici.

Groupe de processus = "ménage"

Premièrement, il existe un groupe de processus comme base du processus Linux. Il s'agit d'un concept de processus de type «ménage» qui regroupe les processus des parents, des enfants et des petits-enfants. Par exemple, si vous lancez le processus de nœud oya.js dans Bash, ko.js et mago.js fork () à partir de là appartiennent au même groupe de processus et reçoivent le même ID de groupe.

Si vous vérifiez l'ID de groupe (GPID) avec la commande ps, vous pouvez voir que le même ID de groupe est en fait attribué aux trois processus de nœud:

$ 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

Comme vous pouvez le voir à partir de ce résultat, le GPID est le même que l'ID de processus (PID) de oya.js. En d'autres termes, le PID du parent devient le GPID du descendant.

Comment tuer l'ensemble du processus "ménage"

Dans Node.js, vous pouvez spécifier l'ID de groupe pour terminer le processus. Tout ce que vous avez à faire est de transmettre le GPID à [process.kill ()]. À ce moment, donnez un nombre négatif. Notez que passer un nombre positif ne "kill ()" que les processus individuels, pas les groupes de processus.

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

À propos, lorsque Ctrl-C est pressé dans le shell, le parent, l'enfant et le petit-enfant sont tous terminés car le SIGINT envoyé par Ctrl-C est envoyé au groupe de processus, pas au processus parent. Parce que c'est. (Source requise)

détaché = créer un autre ménage

Ce que je veux faire cette fois, c'est tuer () ko.js et mago.js tout en gardant le processus oya.js en vie. Cependant, avec kill () avec GPID spécifié, oya.js sera terminé. Parce que les trois ont le même GPID:

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

Vous devez attribuer différents GPID pour ko.js et mago.js. Pour ce faire, spécifiez détaché comme option pour fork ().

oya.js


//Démarrer le processus enfant
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true})

Si cela est spécifié, ko.js et mago.js seront, pour ainsi dire, des «ménages séparés» et appartiendront à des groupes de processus différents. Vous pouvez voir que le GPID est également attribué différemment de 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

Forme complète de oya.js qui tue les descendants du processus

Sur la base de ce qui précède, si vous modifiez oya.js afin que les processus enfants et les processus petits-enfants puissent être arrêtés en même temps, ce sera comme suit:

oya.js


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

//Lorsque SIGINT est accepté
process.on('SIGINT', () => {
  console.log('oya.js: SIGINT')
  process.exit()
})

//Quand le processus se termine
process.on('exit', () => {
  console.log('oya.js: exit')
})

//Démarrer le processus enfant
const ko = require('child_process')
  .fork(__dirname + '/ko.js', [], {detached: true}) //Changements importants!

//Ko après 3 secondes.Quitter js
setTimeout(() => {
  console.log('oya.js: ko.Je termine js...')
  process.kill(-ko.pid, 'SIGINT') //Changements importants!
}, 30000)

// ko.Quand js a fini
ko.on('exit', () => {
  console.log('> Ctrl-Appuyez sur C...')
})

//La magie pour que ce processus continue de fonctionner pour toujours
setInterval(() => null, 10000)

Enfin, exécutons ce oya.js et voyons si ko.js et mago.js sont terminés ensemble:

$ node oya.js
oya.js: running
ko.js: running
mago.js: running
oya.js: ko.Je termine js...
mago.js: SIGINT
ko.js: SIGINT
mago.js: exit
ko.js: exit
> Ctrl-Appuyez sur C...
^Coya.js: SIGINT
oya.js: exit

Comme prévu, ko.js et mago.js ont reçu SIGINT en même temps et ont terminé. Vous pouvez également voir que oya.js est actif jusqu'à ce que vous appuyiez sur Ctrl-C.

Ce qui précède explique comment tuer les descendants du processus lancé par child_process.fork () de Node.js.

Recommended Posts

Node.js: Comment tuer les descendants d'un processus démarré par child_process.fork ()
J'ai essayé de résumer brièvement la procédure de démarrage du développement de Django
Comment afficher une liste des versions installables avec pyenv
Comment démarrer avec Scrapy
Comment démarrer avec Python
Comment démarrer avec Django
Comment insérer un processus spécifique au début et à la fin de l'araignée avec la tremblante
Comment ajouter un package avec PyCharm
[EC2] Comment faire une capture d'écran de votre smartphone avec du sélénium
[Introduction à Python] Comment trier efficacement le contenu d'une liste avec le tri par liste
Comment étudier jusqu'à ce qu'un débutant en statistique se lance avec les statistiques bayésiennes
Résumé de la création d'un environnement LAMP + Wordpress avec Sakura VPS
Comment calculer la volatilité d'une marque
Comment lire un fichier CSV avec Python 2/3
Comment envoyer un message à LINE avec curl
Comment dessiner un graphique à 2 axes avec pyplot
Comment développer une application de panier avec Django
Comment créer un dictionnaire avec une structure hiérarchique.
Comment spécifier des attributs avec Mock of Python
Procédure de création d'application multi-plateforme avec kivy
Zubu amateur veut démarrer Python
[Python] Comment supprimer facilement un processus enfant lancé par multiprocessus à partir d'un autre processus
Comment obtenir une liste de fichiers dans le même répertoire avec python
[Introduction à Python] Comment obtenir l'index des données avec l'instruction for
Une nouvelle forme d'application qui fonctionne avec GitHub: Comment créer des applications GitHub
Comment traiter les images de caméra avec Teams et Zoom
Comment convertir / restaurer une chaîne avec [] en python
Remarques sur l'utilisation d'AIST Spacon ABCI
[Python] Comment dessiner un graphique linéaire avec Matplotlib
Comment créer un sous-menu avec le plug-in [Blender]
Comment identifier l'élément avec le plus petit nombre de caractères dans une liste Python?
Comment obtenir un utilisateur connecté avec les forms.py de Django
Comment convertir un objet de classe en dictionnaire avec SQLAlchemy
Comment faire un jeu de tir avec toio (partie 1)
Comment écrire un type liste / dictionnaire de Python3
Comment démarrer avec Visual Studio Online ~ La fin de l'ère de la construction d'environnement ~
Comment générer un CSV d'en-tête multiligne avec des pandas
Comment déduire l'estimation MAP de HMM avec PyStruct
Bases de PyTorch (2) -Comment créer un réseau de neurones-
Comment déduire une estimation MAP de HMM avec OpenGM
Un mémo sur la façon de surmonter le problème difficile de la capture d'effets avec l'IA
[Python] Comment créer un histogramme bidimensionnel avec Matplotlib
Comment apprendre le SVM structuré de ChainCRF avec PyStruct
[Python] Comment dessiner un diagramme de dispersion avec Matplotlib
Résumé de la façon de partager l'état avec plusieurs fonctions
Exécutez le programme sans créer d'environnement Python! !! (Comment démarrer avec Google Colaboratory)
Comment traiter les images de caméra avec Teams et Zoom Volume de traitement dans le style d'animation
Comment tracer beaucoup de légendes en changeant la couleur du graphique en continu avec matplotlib
Comment déployer une application Web créée avec Flask sur Heroku
Comment créer un BOT Cisco Webex Teams à l'aide de Flask
[Ubuntu] Comment supprimer tout le contenu du répertoire
Comment mettre un lien hypertexte vers "file: // hogehoge" avec sphinx-> pdf
Comment installer NPI + envoyer un message à la ligne avec python
[Python] Comment créer une liste de chaînes de caractères caractère par caractère
Comment convertir un tableau en dictionnaire avec Python [Application]
Comment sortir un document au format pdf avec Sphinx
Une collection de techniques professionnelles compétitives à résoudre avec Python