Ich bin auf ein seltsames Problem gestoßen, bei dem ein ps -o args -p <pid>
Befehl gelegentlich den betreffenden Prozess nicht findet, obwohl er definitiv auf dem betreffenden Server ausgeführt wird. Bei den fraglichen Prozessen handelt es sich um lang laufende Wrapper-Skripte, mit denen einige Java-Apps gestartet werden.
Das "in the wild" -Ereignis des Problems scheint immer früh am Morgen aufzutreten, daher gibt es Hinweise darauf, dass es mit der Festplattenlast auf dem betreffenden Server zusammenhängt, da sie dann ziemlich stark geladen sind, aber durch Ausführen des ps
Eingangs Frage in einer engen Schleife, ich kann schließlich das Problem replizieren - einmal alle paar hundert oder so läuft, erhalte ich eine Fehlermeldung.
Durch Ausführen des folgenden Bash-Skripts habe ich es geschafft, eine Strace-Ausgabe sowohl für einen fehlgeschlagenen als auch für einen erfolgreichen Lauf zu generieren:
while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>
Wenn ich die Ausgabe von fail.out
und vergleiche good.out
, kann ich sehen, dass der getdents
Systemaufruf auf dem Lauf, der irgendwie fehlschlägt, eine viel kleinere Anzahl als die tatsächliche Anzahl von Prozessen auf dem System zurückgibt (in der Größenordnung von ~ 500 im Vergleich zu ~ 1100).
grep getdents good.out
getdents(5, /* 1174 entries */, 32768) = 32760
getdents(5, /* 31 entries */, 32768) = 992
getdents(5, /* 0 entries */, 32768) = 0
grep getdents fail.out
getdents(5, /* 673 entries */, 32768) = 16728
getdents(5, /* 0 entries */, 32768) = 0
... und diese kürzere Liste enthält nicht die tatsächlich fragliche PID, daher wird sie nicht gefunden.
Sie können diesen Abschnitt ignorieren. Die ENOTTY-Fehler werden durch den Kommentar von dave_thompson unten erklärt und stehen in keinem Zusammenhang
Darüber hinaus werden bei dem fehlgeschlagenen Lauf einige
ENOTTY
Fehler angezeigt, die im erfolgreichen Lauf nicht angezeigt werden. Nahe dem Anfang der Ausgabe sehe ichioctl (1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (unangemessenes ioctl für Gerät) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (unangemessenes ioctl für Gerät)
Und am Ende sehe ich eine Single
ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (Unangemessenes ioctl für Gerät)
Der Fehler
ioctl
am Ende tritt direkt vor derps
Rückgabe auf, aber er tritt auf, nachdem derps
bereits eine leere Ergebnismenge gedruckt hat, sodass ich nicht sicher bin, ob sie zusammenhängen. Ich weiß, dass sie in allen fehlgeschlagenen Strace-Ausgaben, die ich habe, konsistent sind, aber nicht in den erfolgreichen erscheinen.
Ich habe absolut keine Ahnung, warum ich getdents
gelegentlich nicht die vollständige Liste der Prozesse finden würde, und jetzt bin ich an dem Punkt angelangt, an dem ich nur noch ein Pflaster auf das Ganze legen werde, indem ich das Steuerungsskript ändere, das das Wrapper-Skript überprüft in Frage, das ps
zweite Mal anzurufen, wenn das erste fehlschlägt, aber ich wäre interessiert zu wissen, ob jemand irgendwelche Ideen hat, was hier los ist.
Auf dem betreffenden System wird Kernel 4.16.13-1.el7.elrepo.x86_64 unter CentOS 7 und Procps-ng Version 3.3.10-17.el7_5.2.x86_64 ausgeführt
>/dev/null
den Aufruf 'fehlgeschlagen' (in der Schleife), aber nicht den Aufruf 'gut', daher die ENOTTY auf fd 1.Antworten:
Lesen Sie die benötigten Informationen direkt aus dem
/proc
Dateisystem und nicht über ein Tool wieps
. Sie finden die gesuchten Informationen ("args") in der Datei/proc/$pid/cmdline
, die nur durch NUL-Bytes anstelle von Leerzeichen getrennt sind.Sie können diesen
sed
Einzeiler verwenden, um die Argumente des Prozesses zu erhalten$pid
:Dieser Befehl entspricht:
(Wenn Sie
args=
in verwenden,ps
wird der Header weggelassen.)Der
sed
Befehl sucht zuerst nach dem letzten nachfolgenden NUL-Byte und ersetzt es durch eine neue Zeile. Anschließend werden alle anderen NUL-Bytes (die einzelne Argumente trennen) durch Leerzeichen ersetzt, um schließlich das gleiche Format zu erzeugen, aus dem Sie sehenps
.In Bezug auf das Auflisten von Prozessen im System
ps
erfolgt dies durch Auflisten von Verzeichnissen in/proc
, aber es gibt inhärente Race-Bedingungen für dieses Verfahren, da Prozesse während derps
Ausführung gestartet und beendet werden. Sie erhalten also nicht wirklich eine Momentaufnahme, sondern eine Annäherung. Insbesondere ist es möglich, dassps
Prozesse angezeigt werden, die zum Zeitpunkt der Anzeige der Ergebnisse bereits beendet wurden, oder Prozesse weggelassen werden, die während der Ausführung gestartet wurden (aber vom Kernel beim Auflisten des Inhalts von nicht zurückgegeben wurden/proc
).Ich bin immer davon ausgegangen, dass ein Prozess, der vor dem
ps
Start vorhanden ist und nach dessen Abschluss noch vorhanden ist, von ihm nicht übersehen wird. Ich habe davon ausgegangen, dass der Kernel garantiert, dass diese immer enthalten sind, selbst wenn viele andere Prozesse abwandern geschaffen und zerstört werden. Was Sie beschreiben, impliziert, dass dies nicht der Fall ist. Ich bin immer noch skeptisch, aber angesichts der bekannten Rennbedingungen, wie esps
funktioniert, ist es zumindest plausibel, dass die Auflistung von PIDs/proc
aufgrund dieser Rennbedingungen möglicherweise eine vorhandene verpasst.Es wäre möglich, dies durch Überprüfen der Quelle des Linux-Kernels zu überprüfen, aber ich habe das (noch) nicht getan, kann also nicht wirklich sicher sagen, ob eine solche Race-Bedingung vorliegt, die einen lang laufenden Prozess verpassen würde, wie z du beschreibst.
Der andere Teil ist die Art und Weise, wie es
ps
funktioniert. Selbst wenn Sie eine einzelne PID mit dem-p
Argument übergeben, werden alle vorhandenen PIDs aufgelistet, obwohl Sie nur an dieser einzelnen PID interessiert sind. In diesem Fall könnte es definitiv eine Verknüpfung geben und die Auflistung der Einträge überspringen/proc
und direkt zu gehen/proc/$pid
.Ich kann nicht sagen, warum es so implementiert wurde. Vielleicht, weil die meisten
ps
Optionen "Filter" für die Prozesse sind, war die Implementierung auf-p
die gleiche Weise einfacher. Wenn Sie eine Verknüpfung verwenden, um direkt zu ihnen zu gelangen, ist/proc/$pid
möglicherweise ein separater Codepfad oder eine Codeduplizierung erforderlich. Eine andere Hypothese lautet, dass einige Fälle-p
plus zusätzliche Optionen enthalten Am Ende ist eine Auflistung erforderlich, daher ist es möglicherweise komplex zu bestimmen, in welchen Fällen die Verknüpfung verwendet werden kann und in welchen nicht.Das bringt uns zur Problemumgehung, direkt zu
/proc/$pid
, ohne den vollständigen Satz von PIDs des Systems aufzulisten, alle bekannten Rennen zu vermeiden und einfach die Informationen, die Sie benötigen, direkt von der Quelle zu erhalten.Es ist ein bisschen hässlich, aber das von Ihnen beschriebene Problem besteht tatsächlich. Es sollte eine zuverlässige Möglichkeit sein, diese Informationen abzurufen.
quelle