Warum konnte ps * sehr * gelegentlich keinen gültigen Prozess finden?

9

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 psEingangs 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.outund vergleiche good.out, kann ich sehen, dass der getdentsSystemaufruf 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 ENOTTYFehler angezeigt, die im erfolgreichen Lauf nicht angezeigt werden. Nahe dem Anfang der Ausgabe sehe ich

ioctl (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 ioctlam Ende tritt direkt vor der psRückgabe auf, aber er tritt auf, nachdem der psbereits 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 getdentsgelegentlich 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 pszweite 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

James
quelle
1
Zu Ihrer Information, die Ioctls haben mit dem Abrufen von Terminaleinstellungen zu tun (z. B. besteht die erste darin, die Anzahl der Zeilen und Spalten zu ermitteln) - es ist also seltsam, dass sie fehlschlagen, aber wahrscheinlich keine direkte Ursache. Das klingt wie ein Kernel-Fehler ...
derobert
2
verwandte Forschung von OpenBSD: https.www.google.com.tedunangst.com/flak/post/…
Thrig
2
Sie haben >/dev/nullden Aufruf 'fehlgeschlagen' (in der Schleife), aber nicht den Aufruf 'gut', daher die ENOTTY auf fd 1.
dave_thompson_085
Oh verdammt. Danke, dass du diesen einen Dave erwischt hast, das erklärt sicherlich die ENOTTYs.
James
Ich bin froh zu sehen, dass ich nicht der einzige bin, der dieses Problem hat. Die Art und Weise, wie ich das umgehen kann, ist ein Try-Catch, der erneut versucht, wenn der Befehl fehlschlägt, was jedoch immer noch ärgerlich ist: /
Josh Correia

Antworten:

7

Lesen Sie die benötigten Informationen direkt aus dem /procDateisystem und nicht über ein Tool wie ps. 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 sedEinzeiler verwenden, um die Argumente des Prozesses zu erhalten $pid:

sed -e 's/\x00\?$/\n/' -e 's/\x00/ /g' "/proc/$pid/cmdline"

Dieser Befehl entspricht:

ps -o args= -p "$pid"

(Wenn Sie args=in verwenden, pswird der Header weggelassen.)

Der sedBefehl 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 sehen ps.


In Bezug auf das Auflisten von Prozessen im System pserfolgt dies durch Auflisten von Verzeichnissen in /proc, aber es gibt inhärente Race-Bedingungen für dieses Verfahren, da Prozesse während der psAusführung gestartet und beendet werden. Sie erhalten also nicht wirklich eine Momentaufnahme, sondern eine Annäherung. Insbesondere ist es möglich, dass psProzesse 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 psStart 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 es psfunktioniert, ist es zumindest plausibel, dass die Auflistung von PIDs /procaufgrund 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 psfunktioniert. Selbst wenn Sie eine einzelne PID mit dem -pArgument ü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 /procund direkt zu gehen /proc/$pid.

Ich kann nicht sagen, warum es so implementiert wurde. Vielleicht, weil die meisten psOptionen "Filter" für die Prozesse sind, war die Implementierung auf -pdie gleiche Weise einfacher. Wenn Sie eine Verknüpfung verwenden, um direkt zu ihnen zu gelangen, ist /proc/$pidmöglicherweise ein separater Codepfad oder eine Codeduplizierung erforderlich. Eine andere Hypothese lautet, dass einige Fälle -pplus 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.

filbranden
quelle
2
Vielen Dank für dieses Filipe. Ich habe ein Upvoting durchgeführt, weil der Befehl sed nützlich ist (und ich unsere Skripte so geändert habe, dass sie nur in / proc nachsehen) und weil ich nicht wusste, dass das Hinzufügen eines '=' zum ps den Header löschen würde . Ich habe die Antwort nicht akzeptiert, weil ich immer noch sehr neugierig bin, warum nicht die gesamte Liste von / proc angezeigt wird, und ich hoffe, dass jemand anderes es weiß :)
James