Angenommen, Sie haben beispielsweise ein Shell-Skript ähnlich dem Folgenden:
longrunningthing &
p=$!
echo Killing longrunningthing on PID $p in 24 hours
sleep 86400
echo Time up!
kill $p
Sollte den Trick machen, sollte es nicht? Abgesehen davon, dass der Prozess möglicherweise vorzeitig beendet wurde und die PID wiederverwendet wurde, bedeutet dies, dass ein unschuldiger Job stattdessen eine Bombe in der Signalwarteschlange hat. In der Praxis spielt dies möglicherweise eine Rolle, aber es macht mir trotzdem Sorgen. Langfristig etwas zu hacken, um von selbst tot zu fallen oder seine PID auf dem FS zu behalten / zu entfernen, würde genügen, aber ich denke an die allgemeine Situation hier.
killall
welche Übereinstimmungen mit dem Namen übereinstimmen. Zumindest beenden Sie also nur einen Prozess mit demselben Namen wielongrunningthing
. Vorausgesetzt, Sie haben immer nur einen davon zur gleichen Zeit am Laufen.Antworten:
Am besten verwenden Sie den
timeout
Befehl, wenn Sie ihn haben, der dafür vorgesehen ist:Die aktuelle (8.23) GNU-Implementierung funktioniert zumindest mit
alarm()
oder gleichwertig, während auf den untergeordneten Prozess gewartet wird. Es scheint nicht zu verhindern, dass der AlarmSIGALRM
zwischen derwaitpid()
Rückkehr und demtimeout
Verlassen ausgelöst wird ( wodurch dieser Alarm effektiv gelöscht wird ). Während dieses kleinen Fensterstimeout
können sogar Nachrichten auf stderr geschrieben werden (zum Beispiel, wenn das Kind einen Core gelöscht hat), wodurch das Race-Fenster weiter vergrößert wird (auf unbestimmte Zeit, wenn stderr zum Beispiel eine vollständige Pipe ist).Ich persönlich kann mit dieser Einschränkung leben (die wahrscheinlich in einer zukünftigen Version behoben wird).
timeout
Außerdem wird besonders darauf geachtet, den korrekten Ausgangsstatus zu melden, andere Eckfälle (wie SIGALRM beim Start blockiert / ignoriert, andere Signale ...) besser zu behandeln, als Sie es wahrscheinlich von Hand schaffen würden.Als Annäherung könnte man es so schreiben
perl
:Es gibt einen
timelimit
Befehl unter http://devel.ringlet.net/sysutils/timelimit/ (timeout
einige Monate vor GNU ).Dieser verwendet einen
alarm()
ähnlichen Mechanismus, installiert jedoch einen HandlerSIGCHLD
(ignoriert angehaltene Kinder), um das sterbende Kind zu erkennen. Außerdem wird der Alarm vor dem Ausführen abgebrochenwaitpid()
(dh die Zustellung wird nicht abgebrochen,SIGALRM
wenn er aussteht, aber ich sehe kein Problem darin, dass er so geschrieben ist) und vor dem Aufrufen abgebrochenwaitpid()
(eine wiederverwendete PID kann nicht getötet werden) ).netpipes hat auch einen
timelimit
Befehl. Diese Methode ist um Jahrzehnte älter als alle anderen, hat jedoch einen anderen Ansatz, funktioniert jedoch bei gestoppten Befehlen nicht ordnungsgemäß und gibt1
bei Zeitüberschreitung einen Beendigungsstatus zurück.Als direktere Antwort auf Ihre Frage können Sie Folgendes tun:
Überprüfen Sie also, ob der Prozess noch ein Kind von uns ist. Wieder gibt es ein kleines Wettlauffenster (zwischen dem
ps
Abrufen des Status dieses Prozesses und demkill
Beenden), in dem der Prozess sterben und seine PID von einem anderen Prozess wiederverwendet werden kann.Mit einigen Muscheln (
zsh
,bash
,mksh
), können Sie Job - Spezifikationen statt pids passieren.Das funktioniert nur, wenn Sie nur einen Hintergrundjob erzeugen (andernfalls ist es nicht immer zuverlässig möglich, die richtige Jobspezifikation zu erhalten).
Wenn das ein Problem ist, starten Sie einfach eine neue Shell-Instanz:
Das funktioniert, weil die Shell den Job vom Job-Tisch entfernt, wenn das Kind stirbt. Hier sollte es kein Rennfenster geben, da zum Zeitpunkt des Shell-Aufrufs
kill()
entweder das SIGCHLD-Signal nicht verarbeitet wurde und die PID nicht wiederverwendet werden kann (da nicht darauf gewartet wurde) oder es verarbeitet wurde und Job wurde aus der Prozesstabelle entfernt (undkill
würde einen Fehler melden).bash
‚skill
zumindest blockiert SIGCHLD , bevor er seine Arbeit Tabelle greift das zu erweitern%
und deblockiert es nach demkill()
.Eine weitere Möglichkeit zu vermeiden , dass mit
sleep
hängenden Prozess auch nach demcmd
gestorben ist , mitbash
oderksh93
ist ein Rohr zu verwenden , mitread -t
stattsleep
:Dieser hat immer noch Rennbedingungen und du verlierst den Exit-Status des Befehls. Es wird auch davon
cmd
ausgegangen, dass fd 4 nicht geschlossen wird.Sie können versuchen, eine rennfreie Lösung
perl
wie folgt zu implementieren :(obwohl es verbessert werden müsste, um andere Arten von Eckkoffern zu handhaben).
Eine andere rennfreie Methode könnte die Verwendung von Prozessgruppen sein:
Beachten Sie jedoch, dass die Verwendung von Prozessgruppen Nebenwirkungen haben kann, wenn ein Endgerät über E / A verfügt. Es hat jedoch den zusätzlichen Vorteil, alle anderen zusätzlichen Prozesse, die durch hervorgerufen werden, zu beenden
cmd
.quelle
timeout
ist nicht portabel, die Antwort erwähnte zuerst eine portable Lösung.jobs
und dann wissen, dass (wie es deine eigene Shell ist, in der du die Kontrolle darüber hast, was als nächstes passiert) der nächste Hintergrund Job wird N + 1 sein? [dann kannst du N retten und später% N + 1 töten])Im Allgemeinen können Sie nicht. Alle bisher gegebenen Antworten sind fehlerhafte Heuristiken. Es gibt nur einen Fall, in dem Sie die pid sicher zum Senden von Signalen verwenden können: Wenn der Zielprozess ein direktes untergeordnetes Element des Prozesses ist, der das Signal sendet, und das übergeordnete Element noch nicht darauf gewartet hat. In diesem Fall wird die pid reserviert (dies ist ein "Zombie-Prozess"), selbst wenn sie beendet wurde, bis der Elternteil darauf wartet. Mir ist keine Möglichkeit bekannt, das mit der Shell sauber zu machen.
Eine andere sichere Möglichkeit, Prozesse abzubrechen, besteht darin, sie mit einem Kontroll-Tty zu starten, das auf ein Pseudo-Terminal eingestellt ist, für das Sie die Masterseite besitzen. Sie können dann Signale über das Terminal senden, z. B. das Zeichen für
SIGTERM
oderSIGQUIT
über die Pty schreiben.Eine weitere Möglichkeit, die für die Skripterstellung praktischer ist, besteht darin, eine benannte
screen
Sitzung zu verwenden und Befehle an die Bildschirmsitzung zu senden, um sie zu beenden. Dieser Vorgang findet über eine Pipe oder einen Unix-Socket statt, die bzw. der gemäß der Bildschirmsitzung benannt ist. Diese wird nicht automatisch wiederverwendet, wenn Sie einen sicheren eindeutigen Namen auswählen.quelle
Wenn Sie den Prozess starten, speichern Sie seine Startzeit:
Bevor Sie versuchen, den Prozess zu beenden, stoppen Sie ihn (dies ist nicht unbedingt erforderlich, aber es ist eine Möglichkeit, Rennbedingungen zu vermeiden: Wenn Sie den Prozess stoppen, kann die PID nicht wiederverwendet werden.)
Überprüfen Sie, ob der Prozess mit dieser PID dieselbe Startzeit hat, und beenden Sie ihn, falls ja, andernfalls lassen Sie den Prozess fortfahren:
Dies funktioniert, weil es auf einem bestimmten Betriebssystem nur einen Prozess mit derselben PID und Startzeit geben kann.
Wenn Sie den Prozess während der Überprüfung anhalten, sind die Rennbedingungen kein Problem. Dies hat offensichtlich das Problem, dass ein zufälliger Prozess für einige Millisekunden angehalten werden kann. Abhängig von der Art des Prozesses kann dies ein Problem sein oder auch nicht.
Persönlich würde ich einfach Python verwenden und
psutil
PID automatisch wiederverwenden:quelle
ps -o start=
Format nach einer Weile von 18:12 auf Jan26 ändert. Hüten Sie sich auch vor DST-Änderungen. Wenn Sie unter Linux arbeiten, werden Sie es wahrscheinlich vorziehenTZ=UTC0 ps -o lstart=
.lstart
, ich bearbeite ihn in.Auf einem Linux-System können Sie sicherstellen, dass eine PID nicht wiederverwendet wird, indem Sie den PID-Namespace beibehalten. Dies kann über die
/proc/$pid/ns/pid
Datei erfolgen.man namespaces
-init
.man pid_namespaces
-util-linux
Paket enthält viele nützliche Tools zum Bearbeiten von Namespaces. Beispiel:unshare
Wenn Sie die Rechte in einem Benutzernamensraum noch nicht festgelegt haben, sind Superuser-Rechte erforderlich:Wenn Sie keinen Benutzernamensraum eingerichtet haben, können Sie dennoch beliebige Befehle sicher ausführen, indem Sie die Berechtigungen sofort löschen. Der
runuser
Befehl ist eine andere (nicht setuid) Binärdatei, die vomutil-linux
Paket bereitgestellt wird und die wie folgt aussehen kann:...und so weiter.
Im obigen Beispiel werden zwei Schalter an
unshare(1)
das--fork
Flag übergeben, das den aufgerufenensh -c
Prozess zum ersten erstellten Kind macht und desseninit
Status sichert , sowie an das--pid
Flag, das anweistunshare(1)
, einen PID-Namespace zu erstellen.Der
sh -c
Prozess erzeugt fünf untergeordnete Shells im Hintergrund - jeweils einewhile
Endlosschleife, die die Ausgabe von so langedate
an das Ende von anfügt ,log
wiesleep 1
true zurückgegeben wird. Nach dem Laichen dieser Prozessesh
Anrufesleep
endet für weitere 5 Sekunden , dann.Es ist vielleicht erwähnenswert, dass, wenn das
-f
Flag nicht verwendet würde, keine der Hintergrundschleifenwhile
enden würde, aber damit ...AUSGABE:
quelle
Überlegen Sie
longrunningthing
, ob Sie sich ein bisschen besser verhalten, ein bisschen dämonhafter. Sie können beispielsweise eine PID-Datei erstellen , die zumindest eine eingeschränkte Kontrolle über den Prozess ermöglicht. Es gibt verschiedene Möglichkeiten, dies zu tun, ohne die ursprüngliche Binärdatei zu ändern, die alle einen Wrapper beinhalten. Zum Beispiel:Ein einfaches Wrapper-Skript, das den erforderlichen Job im Hintergrund startet (mit optionaler Ausgabeumleitung), die PID dieses Prozesses in eine Datei schreibt, dann wartet, bis der Prozess abgeschlossen ist (mit
wait
) und die Datei entfernt. Wenn während des Wartens der Prozess zB durch etwas wie getötet wirdDer Wrapper wird nur sicherstellen, dass die PID-Datei entfernt wird.
Ein Monitor-Wrapper, der seine eigene PID irgendwo ablegt und die an ihn gesendeten Signale auffängt (und darauf reagiert). Einfaches Beispiel:
Nun, wie @R .. und @ StéphaneChazelas betonten, haben diese Ansätze oft irgendwo eine Racebedingung oder beschränken die Anzahl der Prozesse, die Sie erzeugen können. Außerdem werden die Fälle nicht behandelt, in denen sich die
longrunningthing
Maygabel und die Kinder lösen (was wahrscheinlich nicht das Problem in der ursprünglichen Frage war).Mit neueren Linux-Kerneln (lesen Sie ein paar Jahre altes) kann dies gut mit cgroups , dem Freezer , behandelt werden , den einige moderne Linux-Init-Systeme verwenden.
quelle
longrunningthing
ist, dass Sie keine Kontrolle darüber haben, was es ist. Ich habe auch ein Shell-Skript-Beispiel angegeben, weil es das Problem erklärt. Ich mag Ihre und all die anderen kreativen Lösungen hier, aber wenn Sie Linux / bash verwenden, gibt es dafür ein "Timeout". Ich nehme an, ich sollte die Quelle dazu bringen und sehen, wie es das macht!timeout
ist keine eingebaute Shell. Es gab verschiedene Implementierungen einestimeout
Befehls für Linux, eine wurde kürzlich (2008) zu GNU coreutils hinzugefügt (also nicht Linux-spezifisch), und das ist, was die meisten Linux-Distributionen heutzutage verwenden.Wenn Sie unter Linux (und einigen anderen * Nixen) arbeiten, können Sie überprüfen, ob der Prozess, den Sie beenden möchten, noch verwendet wird und ob die Befehlszeile mit Ihrem langen Prozess übereinstimmt. So etwas wie :
Eine Alternative kann darin bestehen, zu überprüfen, wie lange der Prozess, den Sie beenden möchten, mit so etwas wie ausgeführt wird
ps -p $p -o etime=
. Sie könnten es selbst tun, indem Sie diese Informationen aus extrahieren. Dies/proc/$p/stat
wäre jedoch schwierig (die Zeit wird in Sekunden gemessen und Sie müssen die Systembetriebszeit auch in verwenden/proc/stat
).In der Regel können Sie jedoch nicht sicherstellen, dass der Prozess nach Ihrer Überprüfung und vor dem Beenden nicht ersetzt wird .
quelle
cat pidfile
Ergebnis nur unverblümt . Ich kann mich nicht an eine saubere Methode erinnern, die es nur in der Shell macht. Die vorgeschlagene Namespace-Antwort scheint jedochDas ist eigentlich eine sehr gute Frage.
Die Art und Weise, die Eindeutigkeit eines Prozesses zu bestimmen, besteht darin, (a) zu betrachten, wo er sich im Speicher befindet; und (b) was dieser Speicher enthält. Um genau zu sein, möchten wir wissen, wo im Speicher sich der Programmtext für den ersten Aufruf befindet, da wir wissen, dass der Textbereich jedes Threads eine andere Position im Speicher einnimmt. Wenn der Prozess abbricht und ein anderer Prozess mit derselben PID gestartet wird, belegt der Programmtext für den neuen Prozess nicht denselben Speicherplatz und enthält nicht dieselben Informationen.
Machen Sie also gleich nach dem Start Ihres Prozesses
md5sum /proc/[pid]/maps
das Ergebnis und speichern Sie es. Wenn Sie den Prozess später beenden möchten, führen Sie eine weitere MD5-Summe aus und vergleichen Sie sie. Wenn es passt, dann töte die PID. Wenn nicht, dann nicht.Um dies selbst zu sehen, starten Sie zwei identische Bash-Shells. Untersuchen Sie die
/proc/[pid]/maps
für sie und Sie werden feststellen, dass sie unterschiedlich sind. Warum? Denn obwohl es sich um dasselbe Programm handelt, belegen sie unterschiedliche Speicherorte und die Adressen ihres Stapels sind unterschiedlich. Wenn also Ihr Prozess abstürzt und seine PID wiederverwendet wird, obwohl derselbe Befehl mit denselben Argumenten erneut gestartet wird, unterscheidet sich die "Maps" -Datei und Sie werden wissen, dass Sie nicht mit dem ursprünglichen Prozess zu tun haben.Siehe: proc man page für Details.
Beachten Sie, dass die Datei
/proc/[pid]/stat
bereits alle Informationen enthält, die andere Poster in ihren Antworten angegeben haben: Alter des Prozesses, übergeordnete PID usw. Diese Datei enthält sowohl statische als auch dynamische Informationen. Wenn Sie diese Datei also als Basis verwenden möchten, müssen Sie sie verwenden Nach dem Starten vonlongrunningthing
müssen Sie die folgenden statischen Felder aus derstat
Datei extrahieren und zum späteren Vergleich speichern:PID, Dateiname, PID des übergeordneten Elements, Prozessgruppen-ID, steuerndes Terminal, Zeit, die der Prozess nach dem Systemstart gestartet wurde, Größe des residenten Satzes, Adresse des Starts des Stapels,
Zusammengenommen identifizieren die oben genannten den Prozess eindeutig, und so ist dies ein weiterer Weg. Tatsächlich konnte man nur mit "pid" und "time process started after system boot" mit einem hohen Maß an Sicherheit davonkommen. Extrahieren Sie einfach diese Felder aus der
stat
Datei und speichern Sie sie beim Starten Ihres Prozesses. Später, bevor Sie es töten, extrahieren Sie es erneut und vergleichen Sie. Wenn sie übereinstimmen, können Sie sicher sein, dass Sie sich den ursprünglichen Prozess ansehen.quelle
/proc/[pid]/maps
sich im Laufe der Zeit ändert, wenn zusätzlicher Speicher zugewiesen wird oder der Stapel wächst oder neue Dateien zugeordnet werden ... Und was bedeutet unmittelbar nach dem Start ? Nachdem alle Bibliotheken gemappt wurden? Wie stellen Sie das fest?md5sum
auf ihren Karten Dateien. Ich lasse es ein oder zwei Tage laufen und berichte hier mit den Ergebnissen.Eine andere Möglichkeit wäre, das Alter des Prozesses zu überprüfen, bevor er beendet wird. Auf diese Weise können Sie sicherstellen, dass Sie einen Prozess, der nicht in weniger als 24 Stunden gestartet wurde, nicht beenden. Sie können eine darauf
if
basierende Bedingung hinzufügen , bevor Sie den Prozess beenden.Diese
if
Bedingung prüft, ob die Prozess-ID$p
weniger als 24 Stunden (86400 Sekunden) beträgt.PS: - Der Befehl
ps -p $p -o etime=
hat das Format<no.of days>-HH:MM:SS
quelle
mtime
von/proc/$p
hat nichts mit der Startzeit des Prozesses zu tun.if
Zustand zu ändern . Bitte zögern Sie nicht zu kommentieren, wenn es fehlerhaft ist.Was ich tue, ist, nachdem ich den Prozess beendet habe, es erneut zu tun. Jedes Mal, wenn ich das tue, kommt die Antwort zurück: "Kein solcher Prozess."
Einfacher geht es nicht und ich mache das seit Jahren ohne Probleme.
quelle