――Alles in Ruby spiegelt Unix-Systemaufrufe, Kultur und Ideen wider. ――Mit Ruby können Sie die niedrige Ebene der Sprache überlassen und die Idee von Unix selbst lernen.
――Die Idee der Unix-Programmierung und ihrer Technologie wird in den nächsten 40 Jahren nützlich sein.
--man Seite
--Abschnitt der Manpage
--Prozess: Unix-Atom
pid
Eindeutige Kennung (Prozess-ID) aller auf dem System ausgeführten Prozesse
Ein Nummernschild, das keine Informationen über den Prozess enthält, nur eine Seriennummer.
Gegenseitige Referenz Befehl --ps (1)
Die PID verweist auf die Informationen, die der Kernel sieht.
Ich sehe es oft in Protokolldateien.
Wenn Sie mehrere Prozesse in einer Datei protokollieren, müssen Sie identifizieren können, von welchem Prozess jede Zeile in der Protokolldatei stammt. Sie können dieses Problem lösen, indem Sie die PID in jede Zeile des Protokolls einfügen.
Befehle, auf die mit den vom Betriebssystem bereitgestellten Informationen verwiesen werden kann
Zeigen Sie den laufenden Prozess in Echtzeit an.
offene Dateien auflisten. Offene Dateien auflisten.
Jeder Prozess hat einen übergeordneten Prozess.
ppid --Ppid des übergeordneten Prozesses.
Elternprozess
Der Prozess, der den Prozess gestartet hat.
--Beispiel
Wenn Sie "Terminal.app" unter Mac OS X starten, werden Sie zur Bash aufgefordert.
Da alles ein Prozess ist, bedeutet dieses Verhalten, dass Sie den Prozess "Terminal.app" und dann den Bash-Prozess gestartet haben.
Zu diesem Zeitpunkt wird der übergeordnete Prozess des Bash-Prozesses zum Prozess von "Terminal.app".
Wenn Sie den Befehl ls (1) an der Bash-Eingabeaufforderung ausführen, ist der übergeordnete Prozess des ls-Prozesses der Bash-Prozess.
Praktisches Beispiel
Es gibt nicht viele Fälle, in denen ppid tatsächlich verwendet wird, und es kann wichtig sein, wenn Sie einen Daemon-Prozess erkennen möchten.
Die Nummer der geöffneten Datei wird als Dateideskriptor dargestellt, ebenso wie der laufende Prozess durch pid dargestellt wird.
Alles ist eine Datei
Eine der Unix-Philosophie.
Geräte, Sockets, Pipes, Dateien usw. werden als Dateien behandelt.
--Die Dateibeschreibung repräsentiert eine Ressource
--Dateideskriptoren sind dazu bestimmt, mit dem Prozess zu leben und mit dem Prozess zu sterben.
--Dateideskriptoren werden in der Reihenfolge der kleinsten nicht verwendeten Ganzzahl zugewiesen.
Wenn die Ressource geschlossen wird, wird der ihr zugewiesene Dateideskriptor wieder verfügbar.
Standard-Stream
Jeder Unix-Prozess enthält drei offene Ressourcen.
STDIN
Bietet eine allgemeine Methode zum Lesen von Eingaben wie Tastaturgeräten und Pipes.
STDOUT、 STDERR
Bietet eine allgemeine Methode zum Schreiben an Ausgabeziele wie Monitore, Dateien und Drucker.
Praktisches Beispiel --Dateideskriptoren sind das Herzstück der Netzwerkprogrammierung mit Sockets und Pipes.
――Wie viele Dateideskriptoren kann ein Prozess haben?
Abhängig von den Systemeinstellungen.
Der Kernel legt Ressourcenlimits für jeden Prozess fest.
Umgebungsvariable
Der Schlüssel und der Wert sind gepaart und enthalten die Daten, die im Prozess verwendet werden können.
Alle Prozesse erben Umgebungsvariablen von ihrem übergeordneten Prozess. --Umgebungsvariablen werden vom übergeordneten Prozess festgelegt und vom untergeordneten Prozess geerbt. --Umgebungsvariablen sind für jeden Prozess vorhanden und können in jedem Prozess global aufgerufen werden.
--ENV implementiert teilweise Enumerable- und Hash-APIs, hat jedoch nicht genau die gleiche Funktionalität wie Hash.
$ RAILS_ENV=production rails server
$ EDITOR=mate bundle open actionpack
$ QUEUE=default rake resque:work
--Umgebungsvariablen werden häufig verwendet, um Eingaben an Befehlszeilentools zu übergeben.
ARGV
Ein spezielles Array, auf das Ruby-Prozesse verweisen können.
argv
Argument Vektor. Eine Reihe von Argumenten.
Enthält die Argumente, die über die Befehlszeile an den Prozess übergeben wurden.
$ cat argv.rb
p ARGV
$ ruby argv.rb foo bar -va
["foo", "bar", "-va"]
ARGV = Array --Das Argument ist ein Array. Sie können Elemente hinzufügen oder entfernen und den Inhalt der gespeicherten Elemente nach Belieben ändern. --Eine Objektdarstellung der über die Befehlszeile übergebenen Argumente ――Daher gibt es nicht viele Möglichkeiten, tatsächlich gezwungen zu werden, sich zu ändern.
Praktisches Beispiel
Wenn Sie den Dateinamen an das Programm übergeben möchten.
Zum Beispiel beim Schreiben eines Programms, das einen oder mehrere Dateinamen von der Befehlszeile empfängt und die Dateien verarbeitet.
Analyse von Befehlszeilenargumenten
Unix-Prozesse haben nur wenige Möglichkeiten, den Status eines Prozesses zu ermitteln.
Erfindung der Protokolldatei durch den Programmierer.
Protokolldateien können in das Dateisystem geschrieben werden, um alle Informationen zu teilen, die der Prozess vermitteln möchte. ――Aber hier geht es mehr um die Dateisystemebene als um den Prozess selbst. --Öffnen Sie eine Steckdose und nutzen Sie das Netzwerk.
Prozesse können mit anderen Prozessen kommunizieren, aber da sie vom Netzwerk abhängen, unterscheidet sich dies auch von der Ebene des Prozesses selbst.
Zwei Mechanismen zur Übermittlung von Informationen auf Prozessebene.
Code beenden
Das letzte Zeichen auf der Welt am Ende des Prozesses.
Endcode-Wert
Jeder Prozess endet mit einem Endcode (0-255), der angibt, ob er normal oder höher endet.
Endcode 0
Am Ende des Normalen
Andere Abschlusscodes weisen auf einen Fehler hin.
So beenden Sie den Vorgang
Kernel#exit --Der einfachste Weg.
Auch wenn das Skript beendet wird, ohne es explizit zu beenden, wird dieselbe Verarbeitung implizit ausgeführt.
Kernel#exit!
Der Standard-Beendigungscode ist abnormale Beendigung (1)
Der durch Kernel # at_exit definierte Block wird nicht ausgeführt.
Kernel#abort
Wird oft verwendet, um einen problematischen Prozess zu beenden.
Kernel#raise
Eine der Möglichkeiten, den Prozess zu beenden, auch wenn die durch Raise ausgelöste Ausnahme nicht abgefangen wird. --raise beendet den Prozess nicht sofort, Ausnahmen werden einfach gegenüber dem Anrufer ausgelöst.
Wenn die Ausnahme nirgendwo abgefangen wird, wird der Prozess als Ergebnis beendet.
fork(2)
Sie können einen neuen Prozess aus einem laufenden Prozess erzeugen.
Der neue Prozess ist eine vollständige Kopie des ursprünglichen Prozesses.
Eine der mächtigsten Ideen in der Unix-Programmierung.
Prozesserzeugung
Der Prozess, der fork (2) aufruft, wird als "übergeordneter Prozess" und der neu erstellte Prozess als "untergeordneter Prozess" bezeichnet.
--Kinderprozess
Der untergeordnete Prozess erbt alle vom übergeordneten Prozess verwendeten Speicherkopien.
Wenn ein Prozess eine große Menge an Software lädt und 500 MB Speicher belegt (z. B. Rails-App), befindet sich jeder untergeordnete Prozess im Speicher, wenn Sie zwei untergeordnete Prozesse aus diesem Prozess erzeugen. Es wird eine große Kopie der Software effizient aufbewahren.
Mit Fork kommt der Anruf sofort zurück und es gibt drei Prozesse, die 500 MB Speicher verbrauchen.
Es ist sehr praktisch, wenn Sie mehrere Anwendungsinstanzen gleichzeitig starten möchten.
Der vom übergeordneten Prozess geöffnete Dateideskriptor wird auf die gleiche Weise vererbt.
Dem untergeordneten Prozess wird der gleiche Dateideskriptor wie der übergeordnete Prozess zugewiesen.
Daher können Sie geöffnete Dateien, Sockets usw. zwischen den beiden Prozessen freigeben.
Da es sich um einen völlig neuen Prozess handelt, wird ihm eine eindeutige PID zugewiesen. --ppid ist die pid des Prozesses, der fork (2) ausgeführt hat.
Der vom untergeordneten Prozess kopierte Speicher kann frei geändert werden, ohne den übergeordneten Prozess zu beeinflussen.
Gabel Methode
Die Fork-Methode wird einmal aufgerufen und gibt tatsächlich zweimal zurück. --fork ist eine Methode, um einen neuen Prozess zu erzeugen!
Einer kehrt zum aufrufenden übergeordneten Prozess und der andere zum erzeugten untergeordneten Prozess zurück.
#Sowohl die if- als auch die else-Klausel der if-Anweisung werden ausgeführt
#Auf der übergeordneten Prozessseite wird die PID des erstellten untergeordneten Prozesses zurückgegeben, und auf der untergeordneten Prozessseite gibt fork null zurück.
if fork
puts "entered the if block"
else
puts "entered the else block"
end
=> entered the if block
entered the else block
--Ist Fork Multi-Core-Programmierung?
--Verwenden Sie Blöcke
fork do
#Beschreiben Sie hier den Prozess, der im untergeordneten Prozess ausgeführt werden soll
end
#Beschreiben Sie hier den Prozess, der vom übergeordneten Prozess ausgeführt werden soll
Der untergeordnete Prozess lebt weiter, auch wenn der übergeordnete Prozess stirbt.
Wenn Sie beispielsweise einen untergeordneten Prozess erstellen und Strg-C eingeben, funktioniert die Prozesssteuerung möglicherweise nicht, ob der übergeordnete oder der untergeordnete Prozess beendet werden soll.
Verwalten Sie den Orphan-Prozess --Demon-Prozess ――Es ist ein absichtlich verwaister Prozess, der darauf abzielt, für immer in Bewegung zu bleiben.
Unix-Signal
So kommunizieren Sie mit einem Prozess ohne Terminal.
--Kopie beim Schreiben (CoW, Kopie beim Schreiben)
--CoW ist sehr bequem und schnell, um Ressourcen zu sparen, wenn untergeordnete Prozesse mit Fork (2) erzeugt werden.
Sie müssen nur die Daten kopieren, die der untergeordnete Prozess benötigt, und den Rest freigeben.
Damit CoW gut funktioniert, muss die Ruby-Implementierung so geschrieben werden, dass diese vom Kernel bereitgestellte Funktion nicht beeinträchtigt wird.
--Feuer und vergessen --Wenn Sie möchten, dass der untergeordnete Prozess asynchron verarbeitet wird und der übergeordnete Prozess unabhängig fortfahren soll.
message = 'Good Morning'
recipient = '[email protected]'
fork do
#Erstellen Sie einen untergeordneten Prozess und senden Sie Daten an den Statistiksammler
#Der übergeordnete Prozess setzt den eigentlichen Nachrichtenversand fort.
#
#Als übergeordneter Prozess möchte ich nicht, dass sich diese Arbeit verlangsamt
#Es ist mir egal, ob die Übertragung an den Statistiksammler aus irgendeinem Grund fehlschlägt.
StatsCollector.record message, recipient
end
#Senden Sie eine Nachricht an das eigentliche Ziel
--Kinderschutz
Mit Ausnahme der oben genannten Fälle ist in den meisten Fällen unter Verwendung von Fork (2) ein Mechanismus erforderlich, mit dem untergeordnete Prozesse regelmäßig verwaltet werden können.
Process.wait
Blockieren Sie und warten Sie auf den übergeordneten Prozess, bis einer der untergeordneten Prozesse beendet wird. --Process.wait gibt die PID des beendeten untergeordneten Prozesses zurück.
Vorher ändern:
fork do
5.times do
sleep 1
puts "I'm an orphan!"
end
end
abort "Parent process died..."
Nach der veränderung:
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...
Process.wait2
Gibt einen (pid) Rückgabewert zurück
Gibt zwei Rückgabewerte zurück (PID- und Exit-Status)
Der Endstatus wird als Kommunikationsmittel zwischen Prozessen durch den Endcode verwendet.
Der Endcode wird verwendet, um Informationen an andere Prozesse zu übermitteln. Mit Process.wait2 können Sie jedoch direkt auf diese Informationen verweisen.
Process::Status
Der von Process.wait2 zurückgegebene Beendigungsstatus ist eine Instanz der Process :: Status-Klasse.
Das Process :: Status-Objekt enthält viele nützliche Informationen, um genau zu wissen, wie der Prozess beendet wurde.
Beispiel für eine Interprozesskommunikation ohne Dateisystem oder Netzwerk:
#Spawn 5 untergeordnete Prozesse
5.times do
fork do
#Generieren Sie einen zufälligen Wert für jeden untergeordneten Prozess.
#Wenn es gerade ist, gibt es 111 zurück, und wenn es ungerade ist, gibt es 112 als Endcode zurück.
if rand(5)
exit 111
else
exit 112
end
end
end
5.times do
#Warten Sie, bis der erzeugte untergeordnete Prozess abgeschlossen ist.
pid, status = Process.wait2
#Wenn der Endcode 111 ist
#Sie können sehen, dass die auf der untergeordneten Prozessseite generierten Werte gerade sind.
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 und Process.waitpid verweisen beide auf dieselbe Funktion.
Sie können pid an Process.wait übergeben, um auf die Beendigung eines bestimmten untergeordneten Prozesses zu warten, oder Sie können -1 an Process.waitpid übergeben, um auf einen Prozess zu warten. ―― Als Programmierer ist es wichtig, Tools zu verwenden, die die Absicht so weit wie möglich ausdrücken können. Selbst wenn die beiden Methoden identisch sind, ist es besser, sie wie folgt richtig zu verwenden. --Process.wait, um auf einen untergeordneten Prozess zu warten --Process.waitpid beim Warten auf einen bestimmten Prozess
Da der Kernel die Informationen des beendeten Prozesses in die Warteschlange stellt, kann der übergeordnete Prozess die Informationen zum Zeitpunkt der Beendigung des untergeordneten Prozesses immer empfangen.
Daher gibt es kein Problem, auch wenn der übergeordnete Prozess Zeit für die Verarbeitung benötigt, die mit der Beendigung des untergeordneten Prozesses einhergeht.
Praktisches Beispiel ――Die Verwendung von untergeordneten Prozessen ist das häufigste Muster in der Unix-Programmierung.
Genannter Kinderschutzprozess, Meister / Arbeiter, Vorgabel usw. --Erstellen Sie mehrere untergeordnete Prozesse für die parallele Verarbeitung aus einem vorbereiteten Prozess und kümmern Sie sich dann um die untergeordneten Prozesse. --Webserver Einhorn --Unicorn gibt an, wie viele Worker-Prozesse beim Starten des Servers verwendet werden sollen.
Wenn Sie angeben, dass Sie 5 Instanzen benötigen, erzeugt der Einhornprozess 5 untergeordnete Prozesse, um Webanforderungen nach dem Start zu verarbeiten. Der übergeordnete (oder Master-) Prozess überwacht Leben und Tod jedes untergeordneten Prozesses, damit der untergeordnete Prozess ordnungsgemäß reagieren kann.
--Entfernen des untergeordneten Prozesses
Wenn Sie Process.wait nicht verwenden möchten, um auf den Abschluss des untergeordneten Prozesses zu warten, müssen Sie den untergeordneten Prozess trennen.
Der Kernel speichert Informationen über den beendeten untergeordneten Prozess, bis der übergeordnete Prozess Process.wait verwendet, um diese Informationen anzufordern.
Wenn der übergeordnete Prozess den Beendigungsstatus des untergeordneten Prozesses nicht auf unbestimmte Zeit anfordert, werden diese Informationen niemals aus dem Kernel entfernt.
Es ist eine Verschwendung von Kernel-Ressourcen, einen untergeordneten Prozess in einer "Keep Shooting" -Methode zu erstellen und den Beendigungsstatus des untergeordneten Prozesses unbeaufsichtigt zu lassen.
Beispiel:
message = 'Goog Morning'
recipient = '[email protected]'
pid = fork do
#Erstellen Sie einen untergeordneten Prozess und senden Sie die Daten an den Statistiksammler
#Der übergeordnete Prozess setzt den eigentlichen Nachrichtenversand fort.
#
#Als übergeordneter Prozess möchte ich nicht, dass sich diese Arbeit verlangsamt
#Es ist mir egal, ob die Übertragung an den Statistiksammler aus irgendeinem Grund fehlschlägt.
StatsCollector.record message, recipient
end
#Stellen Sie sicher, dass der untergeordnete Prozess, der Statistiken sammelt, kein Zombie wird.
Process.detach(pid)
--Kinderprozesse, die sterben, ohne den übergeordneten Prozess zu überspannen, werden ausnahmslos zu Zombieprozessen.
--Process.wait ist ein blockierender Aufruf --Process.wait ermöglicht es dem übergeordneten Prozess, den untergeordneten Prozess zu verwalten, aber der übergeordnete Prozess kann die Verarbeitung erst fortsetzen, wenn der untergeordnete Prozess beendet ist.
--Beispiel zur Ergänzung von SIGCHLD
child_processes = 3
dead_processes = 0
#Spawn 3 untergeordnete Prozesse
child_processes.times do
fork do
#Schlaf jeweils 3 Sekunden
sleep 3
end
end
#Danach ist der übergeordnete Prozess mit umfangreichen Berechnungen beschäftigt.
#Ich möchte die Beendigung eines untergeordneten Prozesses erkennen.
#Deshalb,:Ergänzen Sie das CHLD-Signal. Auf diese Weise
#Sie können Benachrichtigungen vom Kernel erhalten, wenn ein untergeordneter Prozess beendet wird.
trap(:CHLD) do
#Verarbeiten Sie die Informationen des beendeten untergeordneten Prozesses.Wenn Sie es mit warten bekommen,
#Sie können sehen, welcher der erzeugten untergeordneten Prozesse beendet wurde.
puts Process.wait
dead_processes += 1
#Beenden Sie den übergeordneten Prozess explizit, wenn alle untergeordneten Prozesse beendet wurden.
exit if dead_processes == child_processes
end
#Schwere Berechnungsverarbeitung
loop do
(Math.sqrt(rand(44)) ** 8).floor
sleep 1
end
Parallel zu SIGCHLD
Die Signallieferung ist unzuverlässig.
Wenn ein anderer untergeordneter Prozess während der Verarbeitung eines CHLD-Signals beendet wird, gibt es keine Garantie dafür, dass er das nächste CHLD-Signal erfassen kann.
Behandeln Sie CHLD richtig
Sie müssen den Aufruf von Process.wait wiederholen und warten, bis alle Benachrichtigungen verarbeitet wurden, dass der untergeordnete Prozess beendet wurde.
Zweites Argument von Process.wait
Entspricht der Situation, in der Sie möglicherweise mehrere CHLD-Signale empfangen, während Sie das Signal verarbeiten.
Sie können pid an das erste Argument übergeben, aber Sie können ein Flag an das zweite Argument übergeben, das den Kernel anweist, nicht zu blockieren, wenn keine untergeordneten Prozesse auf das Ende warten.
Process.wait(-1, Process::WNOHANG)
Das Signal wird vom Kernel gesendet.
Das Signal hat eine Quelle.
Signale werden von einem bestimmten Prozess an einen anderen gesendet, und der Kernel fungiert als Vermittler.
Die anfängliche Verwendung von Signalen bestand darin, anzugeben, wie ein Prozess abgebrochen werden soll.
—— Signale sind ein großartiges Werkzeug und funktionieren in bestimmten Situationen hervorragend. ――Aber denken Sie daran, dass das Ergänzen von Signalen dem Verwenden globaler Variablen gleicht.
Der Prozess kann das Signal jederzeit empfangen.
Der Signalempfang ist asynchron. --Wenn ein Prozess ein Signal empfängt, wechselt er zu einem Signalhandler.
Es spielt keine Rolle, ob es sich um eine Besetztschleife oder einen langen Schlaf handelt.
Wenn die gesamte Verarbeitung im Handler abgeschlossen ist, kehrt der Prozess zum unterbrochenen Code zurück und die Verarbeitung wird fortgesetzt.
Wenn Sie die PID kennen, können Sie mit jedem Prozess auf dem System per Signal kommunizieren.
Signale sind ein sehr leistungsfähiges Kommunikationsmittel zwischen Prozessen.
Die Signalübertragung mit Kill (1) vom Terminal ist ein häufiger Anblick.
――Die meisten Signale werden in der realen Welt von Prozessen verwendet, die lange laufen, z. B. Server und Dämonen.
In diesem Fall ist der Absender des Signals eher ein Mensch als ein automatisiertes Programm.
Recommended Posts