――Tout dans Ruby reflète les appels système, la culture et les idées Unix. ―― En utilisant Ruby, vous pouvez laisser le bas niveau au langage et apprendre l'idée d'Unix lui-même.
―― L'idée de la programmation Unix et de sa technologie sera utile pour les 40 prochaines années.
--Appel système
--man page
--section de la page de manuel
--Processus: atome Unix --Tout le code est exécuté sur le processus.
--Référence mutuelle Commande --ps (1) --Le pid renvoie les informations vues par le noyau.
--Commandes pouvant être référencées avec les informations fournies par le système d'exploitation - top(1) --Affichez le processus en cours en temps réel. - lsof(8)
Chaque processus a un processus parent.
ppid --Ppid du processus parent.
--Processus parent
--Exemple --Lorsque vous démarrez "Terminal.app" sur Mac OS X, vous serez invité à bash.
Puisque tout est un processus, ce comportement signifie que vous avez démarré le processus "Terminal.app" puis le processus bash.
À ce moment, le processus parent du processus bash devient le processus de "Terminal.app".
Si vous exécutez la commande ls (1) à partir de l'invite bash, le processus parent du processus ls est le processus bash.
Exemple pratique
Il n'y a pas beaucoup de cas où ppid est réellement utilisé, et cela peut être important si vous voulez détecter un processus démon.
--Tout est un fichier --Une philosophie Unix.
Les périphériques, sockets, tubes, fichiers, etc. sont tous traités comme des fichiers.
La description du fichier représente une ressource
Lorsqu'une ressource est ouverte dans un processus en cours, un descripteur de fichier est attribué.
Les descriptions de fichiers ne sont pas partagées entre des processus indépendants.
Les descriptions de fichiers sont généralement partagées via des relations parent-enfant, mais comment les partager entre des processus totalement indépendants (Utilisation de données auxiliaires dans le socket de domaine UNIX man7 / unix.7.html # lbAD)) est également disponible. (Merci à @ angel_p_57!)
Le descripteur de fichier est détruit lorsque le processus qui a ouvert la ressource se termine.
La description de fichier est libérée lorsque tous les descripteurs de fichier qui font référence à la même description de fichier sont détruits.
Les descripteurs de fichiers sont destinés à vivre avec le processus et à mourir avec le processus.
Dans Ruby, les ressources ouvertes sont représentées par des classes IO. Chaque objet IO connaît le descripteur de fichier qui lui est attribué.
Vous pouvez obtenir le descripteur de fichier en utilisant IO # fileno.
Les descripteurs de fichier sont attribués dans l'ordre à partir du plus petit entier inutilisé. --Lorsque la ressource est fermée, le descripteur de fichier qui lui est attribué redevient disponible.
Flux standard
Chaque processus Unix est livré avec trois ressources ouvertes.
STDIN
Fournit une méthode générale de lecture à partir d'entrées telles que les périphériques clavier et les tuyaux.
STDOUT、 STDERR
Fournit une méthode générale d'écriture vers des destinations de sortie telles que des moniteurs, des fichiers et des imprimantes.
Exemple pratique
Les descripteurs de fichiers sont au cœur de la programmation réseau utilisant des sockets et des tuyaux.
―― Combien de descripteurs de fichier un processus peut-il avoir?
Dépend des paramètres système.
Le noyau fixe des limites de ressources pour chaque processus.
Variable d'environnement
La clé et la valeur sont associées et contiennent les données qui peuvent être utilisées dans le processus.
Tous les processus héritent des variables d'environnement de leur processus parent.
Les variables d'environnement sont définies par le processus parent et sont héritées par le processus enfant.
Des variables d'environnement existent pour chaque processus et sont accessibles globalement dans chaque processus.
--ENV implémente partiellement les API Enumerable et Hash, mais n'a pas exactement les mêmes fonctionnalités que Hash.
$ RAILS_ENV=production rails server
$ EDITOR=mate bundle open actionpack
$ QUEUE=default rake resque:work
ARGV
Un tableau spécial qui peut être référencé par des processus Ruby.
argv
vecteur d'argument. Un tableau d'arguments. --Contient les arguments passés au processus à partir de la ligne de commande.
$ cat argv.rb
p ARGV
$ ruby argv.rb foo bar -va
["foo", "bar", "-va"]
ARGV = Array
L'argument est un tableau, vous pouvez ajouter ou supprimer des éléments, et vous pouvez modifier le contenu des éléments stockés comme vous le souhaitez. --Une représentation objet des arguments passés depuis la ligne de commande «Par conséquent, il n'y a pas beaucoup d'occasions d'être forcé de changer.
Exemple pratique --Si vous souhaitez transmettre le nom du fichier au programme. --Par exemple, lors de l'écriture d'un programme qui reçoit un ou plusieurs noms de fichiers à partir de la ligne de commande et traite les fichiers. --Analyse des arguments de la ligne de commande
--Deux mécanismes pour transmettre des informations au niveau du processus.
$ PROGRAM_NAME
.--Valeur de code de fin
--Fin du code 0
--Comment mettre fin au processus 1. exit 2. exit! 3. abort 4. raise
Kernel#exit
Le moyen le plus simple.
Même si le script est terminé sans le terminer explicitement, le même traitement est implicitement exécuté.
Kernel#exit!
Le code de terminaison par défaut est une terminaison anormale (1)
Le bloc défini par Kernel # at_exit n'est pas exécuté.
Kernel#abort
Souvent utilisé pour mettre fin à un processus problématique.
Kernel#raise
Une des façons de terminer le processus même si l'exception levée par rise n'est pas interceptée. --raise ne termine pas le processus immédiatement, l'exception est simplement levée vers l'appelant.
Si l'exception n'est interceptée nulle part, le processus se terminera en conséquence.
--Génération de processus
Le processus qui appelle fork (2) est appelé le "processus parent", et le processus nouvellement créé est appelé le "processus enfant".
Processus enfant
Le processus enfant hérite de toutes les copies de mémoire utilisées par le processus parent.
Si un processus charge une énorme quantité de logiciels et qu'il consomme 500 Mo de mémoire (ex. Rails app), si vous créez deux processus enfants à partir de ce processus, chaque processus enfant sera en mémoire. Il conservera efficacement une énorme copie du logiciel. --Avec fork, l'appel revient immédiatement et trois processus consomment 500 Mo de mémoire.
C'est vraiment pratique lorsque vous souhaitez lancer plusieurs instances d'application en même temps.
Le descripteur de fichier ouvert par le processus parent est hérité de la même manière.
Le même descripteur de fichier que le processus parent est affecté au processus enfant.
Par conséquent, vous pouvez partager des fichiers ouverts, des sockets, etc. entre les deux processus.
Comme il s'agit d'un processus complètement nouveau, un pid unique lui est attribué. --ppid est le pid du processus qui a exécuté fork (2).
La mémoire copiée par le processus enfant peut être librement modifiée sans affecter le processus parent.
méthode --fork
#Les clauses if et else de l'instruction if sont exécutées
#Du côté du processus parent, le pid du processus enfant créé est retourné, et du côté du processus enfant, fork renvoie nil.
if fork
puts "entered the if block"
else
puts "entered the else block"
end
=> entered the if block
entered the else block
--Utiliser des blocs
fork do
#Décrivez ici le processus à exécuter dans le processus enfant
end
#Décrivez ici le processus à exécuter par le processus parent
Le processus enfant continue à vivre même si le processus parent meurt. --Lorsque vous créez un processus enfant, par exemple, si vous entrez Ctrl-C, le contrôle de processus peut ne pas fonctionner pour savoir si le processus parent ou enfant doit être arrêté.
Gérer le processus orphelin
Processus démon Il s'agit d'un processus délibérément orphelin, visant à continuer à avancer pour toujours.
Signal Unix
Comment communiquer avec un processus qui n'a pas de terminal.
--Copie en écriture (CoW, copie en écriture)
--CoW est très pratique et rapide pour économiser des ressources lors de la création de processus enfants avec fork (2).
--Pour que CoW fonctionne correctement, l'implémentation Ruby doit être écrite de manière à ne pas casser cette fonctionnalité fournie par le noyau.
--Fire et oublier --Lorsque vous souhaitez que le processus enfant se déroule de manière asynchrone et que le processus parent souhaite procéder indépendamment.
message = 'Good Morning'
recipient = '[email protected]'
fork do
#Lancer un processus enfant et envoyer des données au collecteur de statistiques
#Le processus parent continue le processus d'envoi de message réel.
#
#En tant que processus parent, je ne veux pas que ce travail ralentisse
#Je m'en fiche si la transmission au collecteur de statistiques échoue pour une raison quelconque.
StatsCollector.record message, recipient
end
#Envoyer un message à la destination réelle
--Protection de l'enfance
À l'exception des cas ci-dessus, dans la plupart des cas en utilisant fork (2), un type de mécanisme capable de gérer régulièrement les processus enfants est requis.
Process.wait --Bloquez et attendez le processus parent jusqu'à ce que l'un des processus enfants se termine. --Process.wait renvoie le pid du processus enfant terminé.
Changer avant:
fork do
5.times do
sleep 1
puts "I'm an orphan!"
end
end
abort "Parent process died..."
Après le changement:
fork do
5.times do
sleep 1
puts "I am an orphan!"
end
end
Process.wait
abort "Parent process died..."
I am an orphan!
I am an orphan!
I am an orphan!
I am an orphan!
I am an orphan!
Parent process died...
--L'état de fin est utilisé comme moyen de communication entre les processus par le code de fin.
Le code de fin est utilisé pour transmettre des informations à d'autres processus, mais Process.wait2 vous permet de vous référer directement à ces informations.
Process::Status
Le statut de fin renvoyé par Process.wait2 est une instance de la classe Process :: Status. --L'objet Process :: Status contient de nombreuses informations utiles pour savoir exactement comment le processus s'est terminé.
Exemple de communication interprocessus sans système de fichiers ni réseau:
#Spawn 5 processus enfants
5.times do
fork do
#Générez une valeur aléatoire pour chaque processus enfant.
#S'il est pair, il renvoie 111, et s'il est impair, il renvoie 112 comme code de fin.
if rand(5)
exit 111
else
exit 112
end
end
end
5.times do
#Attendez que le processus enfant généré se termine.
pid, status = Process.wait2
#Si le code de fin est 111
#Vous pouvez voir que les valeurs générées côté processus enfant sont paires.
if status.exitstatus == 111
puts "#{pid} encountered an even number!"
else
puts "#{pid} encountered an odd number!"
end
end
favourite = fork do
exit 77
end
middle_child = fork do
abort "I want to be waited on!"
end
pid, status = Process.waitpid2 favourite
puts status.exitstatus
--Process.wait et Process.waitpid pointent tous deux vers la même fonction.
Vous pouvez passer pid à Process.wait pour attendre la fin d'un processus enfant spécifique, ou vous pouvez passer -1 à Process.waitpid pour attendre tout processus. ―― En tant que programmeur, il est important d'utiliser des outils qui peuvent exprimer l'intention autant que possible, donc même si les deux méthodes sont les mêmes, il est préférable de les utiliser correctement comme suit. --Process.wait pour attendre tout processus enfant --Process.waitpid lors de l'attente d'un processus spécifique
Puisque le noyau met en file d'attente les informations du processus terminé, le processus parent peut toujours recevoir les informations au moment de l'arrêt du processus enfant. --Par conséquent, il n'y a pas de problème même si le processus parent prend du temps pour le traitement qui accompagne l'arrêt du processus enfant.
Exemple pratique ―― L'utilisation de processus enfants est le modèle le plus courant dans la programmation Unix.
Processus de protection de l'enfant appelé, maître / travailleur, pré-fourche, etc. --Créez plusieurs processus enfants pour un traitement parallèle à partir d'un processus préparé, puis prenez soin des processus enfants.
Serveur Web Licorne --Unicorn spécifie le nombre de processus de travail à utiliser lors du démarrage du serveur.
Si vous spécifiez que vous avez besoin de 5 instances, le processus licorne génère 5 processus enfants pour gérer les requêtes Web après le lancement. Le processus parent (ou maître) surveille la vie et la mort de chaque processus enfant afin que le processus enfant puisse répondre correctement.
--Détachement du processus enfant
Si vous n'avez pas l'intention d'utiliser Process.wait pour attendre la fin du processus enfant, vous devez détacher le processus enfant.
Le noyau conserve les informations sur le processus enfant terminé jusqu'à ce que le processus parent utilise Process.wait pour demander ces informations.
Si le processus parent ne demande pas indéfiniment l'état de fin du processus enfant, ces informations ne seront jamais supprimées du noyau.
C'est un gaspillage de ressources du noyau que de générer un processus enfant dans une méthode "continuez à tirer" et de laisser l'état de fin du processus enfant sans surveillance.
Exemple:
message = 'Goog Morning'
recipient = '[email protected]'
pid = fork do
#Créer un processus enfant et envoyer les données au collecteur de statistiques
#Le processus parent continue le processus d'envoi de message réel.
#
#En tant que processus parent, je ne veux pas que ce travail ralentisse
#Je m'en fiche si la transmission au collecteur de statistiques échoue pour une raison quelconque.
StatsCollector.record message, recipient
end
#Assurez-vous que le processus enfant qui collecte les statistiques ne devient pas un zombie.
Process.detach(pid)
Process.detach --Création d'un nouveau fil.
Le thread généré attend la fin du processus enfant spécifié par pid.
De cette façon, le noyau n'a pas à garder l'état de sortie inutile à personne.
Les processus enfants qui meurent sans chevaucher le processus parent deviennent des processus zombies sans exception.
Si le processus enfant se termine pendant que le processus parent est en cours de traitement (sans attendre le processus enfant), il deviendra définitivement un zombie. --Une fois que le processus parent obtient le statut de fin du processus zombie, ces informations disparaissent correctement, donc plus de gaspillage de ressources du noyau.
--Process.wait est un appel bloquant --Process.wait permet au processus parent de gérer le processus enfant, mais le processus parent ne peut pas continuer le traitement tant que le processus enfant n'est pas terminé.
--Exemple pour compléter SIGCHLD
child_processes = 3
dead_processes = 0
#Spawn 3 processus enfants
child_processes.times do
fork do
#Dormez pendant 3 secondes chacun
sleep 3
end
end
#Après cela, le processus parent est occupé par des calculs lourds,
#Je souhaite détecter la fin d'un processus enfant.
#Donc,:Complétez le signal CHLD. En faisant cela
#Vous pouvez recevoir des notifications du noyau lorsqu'un processus enfant se termine.
trap(:CHLD) do
#Traiter les informations du processus enfant terminé.Si vous l'obtenez avec attente,
#Vous pouvez voir lequel des processus enfants générés s'est terminé.
puts Process.wait
dead_processes += 1
#Terminez explicitement le processus parent lorsque tous les processus enfants sont terminés.
exit if dead_processes == child_processes
end
#Traitement de calcul lourd
loop do
(Math.sqrt(rand(44)) ** 8).floor
sleep 1
end
--Parallèle avec SIGCHLD
--Gérer correctement CHLD
--Deuxième argument de Process.wait --Correspondance à la situation où vous pouvez recevoir plusieurs signaux CHLD pendant le traitement du signal.
Process.wait(-1, Process::WNOHANG)
--Guide des signaux
--L'utilisation originale des signaux était de spécifier comment tuer un processus.
—— Les signaux sont un excellent outil et fonctionnent très bien dans certaines situations. ――Mais gardez à l'esprit que compléter les signaux revient à utiliser des variables globales.
--Si vous connaissez le pid, vous pouvez communiquer avec n'importe quel processus du système par signal.
――Parlant des signaux dans le monde réel, la plupart d'entre eux sont utilisés par des processus qui continuent à fonctionner pendant une longue période, tels que les serveurs et les démons.
Dans ce cas, l'expéditeur du signal est plus susceptible d'être un humain qu'un programme automatisé.
Recommended Posts