@RalfFriedl:
Ok, Du warst tatsächlich nicht der Adressat dieses Beitrags und in meinem Bestreben, den Hintergrund der Geschichte und die Herleitung zu verdeutlichen ohne allzu sehr abzuschweifen, war ich etwas zu ungenau, um Deinen hohen Anforderungen an die Präzision genügen zu können ... und so habe ich mich tatsächlich beim Unterschied von SIGKILL/SIGTERM (beim Beenden zusammen mit dem Parent) vs. SIGSTOP/SIGTSTP/SIGCONT (wenn das Kind überleben darf) nicht klar genug ausgedrückt - auch wenn sich das für mich auf den ersten Blick noch logisch las, es ist schlicht falsch, wie es da steht ... da hast Du vollkommen recht.
Nur ein Prozess, dessen "session leader" beendet wird und der sich in einem "suspended state" befindet (ob durch SIGSTOP/SIGTSTP oder wegen Wartens auf irgendwelche anderen Ereignisse ist m.W. egal), erhält neben dem SIGHUP als Benachrichtigung über den Tod der Eltern (oder eines Elternteils, er ist ja per Definition Halbwaise) noch ein zusätzliches SIGCONT. Wenn er nicht ohnehin "suspended" ist, kriegt er wohl tatsächlich keine STOP/CONT-Signale, auch nicht zum "Anhalten" vor dem Aufräumen (wenn er ein Todeskandidat ist).
Das ändert aber nichts daran, daß das Skript bei mir auch ohne installierte Trap für SIGHUP (und auch ohne alle anderen Trap-Handler) nicht beim Beenden von rc.S gekillt wird ... dann kann/muß es da also noch einen anderen SIGHUP-Handler geben. Der Ablauf ist ja eigentlich vollkommen korrekt, es sollte zuerst ein SIGHUP an eine "orphaned process group" gehen und dann - wenn auf dieses Signal nicht mit dem Beenden reagiert wird - ein SIGCONT als Zeichen für die Fortsetzung. Wenn man das Skript von einer Terminal-Session aus mit C-Z anhält und dann per fg/bg weiter arbeiten läßt, wird auch TSTP/CONT signalisiert, daher hatte ich erwartet, daß auch hier ein CONT an das Skript signalisiert (und von diesem dann protokolliert) wird, aber es ist wohl nicht "angehalten", sondern nur nicht "an der Reihe" zum Zeitpunkt der Beendigung des Parent-Prozesses (wobei ich die Umsetzung von "sleep" auch irgendwie über STOP/CONT/ALRM erwartet hätte, allerdings ohne es zu überprüfen).
Wenn nun tatsächlich für die Behandlung von SIGHUP in der stdlib keinerlei Vorsorge getroffen wird (das würde dann ja auch erklären, warum ich in den uClibc-Quellen dazu nichts gefunden habe auf Anhieb bei meiner Suche), dann
könnte ja der Unterschied in der Behandlung durch den Kernel liegen ... das hatte ich auch ins Kalkül gezogen - daher die Frage "wer ist verantwortlich", bis mich die Beschreibung der "Termination Internals" bei der glibc erst einmal zufrieden stellte (und dann vielleicht auch auf falsche Überlegungen brachte). Wurden denn da Änderungen im 2.6-Kernel vorgenommen zwischen 2.6.28 und 2.6.32 - das wäre dann die zu klärende Frage.
Das ist nicht korrekt, zumindest nicht die Begründung. Wenn der Parent-Prozess beendet wird (in diesem Fall 149), dann wird die Parent-PID grundsätzlich auf 1 gesetzt, unabhängig davon, ob dies dies auch der Parent-Pzozess von PID 149 war.
Ich hoffe tatsächlich, daß Du bereit bist, das unter "Herleitung" (Beobachtungen -> Überlegungen -> These -> Verifikation) zu verbuchen, denn das Zitat aus der glibc-Doc später in meinem Beitrag sagt das ja auch schon und das hatte ich tatsächlich gelesen und auch (für mich) richtig übersetzt und verstanden.
Das "Verstehen" ist allerdings bei der Verantwortung für das Aufsetzen einer sighandler-Kette für Shell-Skripte in der Busybox mit uClibc im Speziellen (nicht im Allgemeinen) tatsächlich nicht der Fall ist, denn die uClibc bietet schon einige "Spezialitäten" ggü. der glibc z.B. beim
execvp(e) (ab Zeile 253) - der Aufruf von "/bin/sh" mit der angegebenen Datei (wenn die im cwd existiert und nx ist) als "Workaround" ist jedenfalls nicht der "Normalfall" (und obendrein voller Gottvertrauen, daß da die richtige Shell schon irgendwie verlinkt sein wird - aber bestimmt ist das irgendwo dokumentiert und ich habe es nur nicht gefunden, das wäre auch möglich).
Die Abweichende Behandlung von SIGHUP kommt deswegen, weil das Skript einen SIGHUP Handler installiert. Dieser schreibt eine Ausgabe ins Protokoll und insbesondere beendet er den Prozess nicht, daher läuft dieser weiter. Wenn kein Handler installiert wird, dann wird der Prozess beendet. Der SIGHUP Handler im Skript macht kein "detach", sondern beendet nur den Prozess nicht. SIGSTOP hat mit dem Ganzen hier nichts zu tun.
Ok, das habe ich oben vorweg genommen ... ich fasse noch einmal zusammen:
Wenn es tatsächlich nur eine Frage der "exit"-Behandlung im Kernel ist (weil niemand vorher irgendwo einen SigHandler installiert hat), dann müßte ja zwischen den Kernel-Versionen (2.6.28.xx bei 7390 und 2.6.32.xx bei 7490) ein Unterschied bestehen. Zur Busybox-These komme ich gleich noch ...
Die Behandlung von SIGHUP ist nicht Aufgabe der Standard-Library, und die Dokumentation von uClibc sagt deswegen zu dem Thema nichts aus, weil es nichts mit der Library zu tun hat, sondern mit dem Kernel. Die Referenz oben beschreibt das Verhalten des Kernels.
Hat sich das Verhalten des Kernels denn geändert? Nur Deines Wissens, Du mußt meinetwegen jetzt nicht suchen, obwohl ich auch nicht fündig geworden bin ... habe aber auch noch keine Quellen dafür gesucht und verglichen, nur eine Suchmaschine mit wechselnden Begriffen dazu befragt.
Du beschreibst das Setzen der PPID eines "orphaned childs" auf die init-PID als Standardverhalten des
Kernels - ich nehme mal an, das kann dann ja nur die direkte Reaktion auf den "exit"-Syscall für den Parent-Prozess sein, anders erhält der Kernel ja keine Kenntnis vom (geplanten) Ableben eines Prozesses.
Dann müßte ja aber jeder Prozess, der seine eigenen Childs nicht selbst ordentlich abräumt, immer zusätzliche Childs von init hinterlassen - und die würden dann nach meinem Verständnis sogar noch an der Resourcenzuteilung teilnehmen. Ist das tatsächlich so? :gruebel: Wenn der beendete Prozess ein "session leader" ist, wird er m.E. anders behandelt, als wenn er keiner ist - ich bin mir aber tatsächlich nicht sicher, wer dafür die Verantwortung trägt.
Wer räumt denn dann (außer der stdlib-atexit-Behandlung, wenn man keine eigene installiert) hinter einem abgestürzten Prozess auf? Nicht alle Childs werden ja über entsprechende Exceptions sterben, wenn sie irgendwie auf geschlossene Files u.a. zugreifen.
Wie kommen dann "zombies" zustande, also Prozesse, deren Parent nicht mehr existiert/beendet wurde, die aber ihrerseits auf ein Kill-Signal nicht richtig reagieren, weil irgendwo ein anderer Deadlock o.ä. die Behandlung des Signals verhindert?
Theoretisch spielen doch in diesem Kontext auch zwei zusätzliche Syscalls eine Rolle, oder? Einmal setpgid für die Zuweisung eines Prozesses zu einer anderen Prozess-Gruppe (er bleibt aber weiterhin Child der Session und erhält die Signalisierung beim Ende der Session) und setsid für die Zuweisung zu einer anderen Session, die auch dann "überlebt", wenn die startende Session ihre Prozess-Gruppen abräumt, weil sie beendet wird.
Was wird da nun tatsächlich verwendet und gibt es einen Unterschied zwischen "source" und "sh", wenn dann im ausgeführten Script mit JobControl (das ist das & am Ende ja) eine neue Prozess-Gruppe erstellt wird oder liegt das am Ende doch nur am Parent des aufrufenden Prozesses (konkret für die FRITZ!Box, nicht für ein beliebiges System)?
Und die Behandlung (nicht das Senden, das mag tatsächlich vom exit-Syscall erfolgen) von SIGHUP unter der Beachtung des Bedingungen für Foreground-/Background-Jobs (also "POSIX job control") ist dann nach meinem Verständnis trotzdem Sache der Busybox oder der stdlib. Wenn bei identischer Busybox (deshalb die Bitte um einen Test mit anderer stdlib) ein abweichendes Verhalten festzustellen
wäre, würde ich die Ursache irgendwie schon in der stdlib suchen. Warum wäre das falsch?
Der Unterschied wird sein, dass es sich um verschiedene Versionen der Busybox handelt.
Das gilt vielleicht noch für eisbaerin vs. meine Tests ... bei Freetz für 7490/06.2x vs. mein Test dürfte das eher nicht zutreffen, denn es sind beide BB 1.22.1 und ich finde auch keine passende BB-Option. Der Kernel ist da auch derselbe (auch wenn der TE nicht geschrieben hat, daß er
kein "replace kernel" verwendet, unterstelle ich das einfach mal) und damit fällt für meine Begriffe eine unterschiedliche Behandlung des exit-Syscalls (oder ein atexit-Hook auf Kernel-Ebene bzw. in der Busybox) ebenfalls aus. Also bleibt für mich nur noch ein Unterschied in einer dazwischen liegenden "Schicht" (also zwischen BB und Kernel) und da erschien/erscheint mir die Verantwortung der stdlib (egal welcher) durchaus plausibel.
Wie erklärst Du Dir das unterschiedliche Verhalten ansonsten (gesetzt den Fall, der TE hat tatsächlich keinen eigenen Kernel)?
Es kommen hauptsächlich zwei mögliche Ursachen in Frage: in der einen Version wird entweder SIGHUP nicht gesendet oder es wird ignoriert, in der andere wird SIGHUP gesendet und nicht ignoriert.
Das ist sicherlich ein interessanter Punkt. Ob SIGHUP gesendet wird und da tatsächlich eine Behandlung stattfindet, läßt sich ja mit dem Test-Skript durchaus feststellen. Wenn der Trap-Handler das Signal "erschöpfend" bearbeitet und der Abbruch mit diesem Handler nicht erfolgt, wäre ein entsprechender SigHandler plausibel, der die Verarbeitung des Prozesses ansonsten beenden würde (natürlich mit TERM/KILL und nicht mit STOP, das war falsch (mindestens unklar) formuliert). Wenn auch da das Skript nach Start aus der rc.custom beendet wird, liegt die Ursache nicht in einer (fehlenden) Behandlung von SIGHUP.
Der Unterschied kommt vielleicht auch erst später? Die rc.mod wird noch per "source"-Statement vom init-Prozess (bzw. von rc.S) aufgerufen. Bis dahin dürfte der Unterschied zwischen den Rechten der S99-Files in /etc/init.d noch keine Rolle spielen (S99-zzz-rcmod hat kein x gesetzt), da dort auch kein execvp benutzt werden dürfte (ich rechne fest damit, daß da nur bis zum EOF der Datei stdin für den Interpreter ersetzt wird, aber auch das ist nicht verifiziert).
Der entscheidende Unterschied
könnte jedoch der explizite Aufruf der rc.custom über "sh rc.custom" sein, da wird dann nichts mehr ge"source"d. Das erklärt dann aber (wenn es nicht andere zusätzliche Faktoren gibt) wieder nicht den Unterschied 7390/7490, denn das ändert sich im Freetz-Mod ja nicht.
Ich könnte mir aber immer noch
vorstellen (deshalb will ich ja darüber diskutieren und mich nicht nur wie ein Bekloppter durch irgendwelche Quellen wühlen), daß es tatsächlich einen Unterschied macht, ob der Parent-Prozess direkt von init gestartet wurde (wie bei rc.S-Abarbeitung in der FRITZ!Box) oder ob da noch ein Session-Manager (ich nenne den telnetd oder einen sshd o.ä. jetzt mal so, es geht um die Verwaltung von (Pseudo-)Terminals) "dazwischen" ist. Spätestens beim Überblick über die aktiven Background-Jobs kommt dann ja wieder eine unterschiedliche Ende-Behandlung zwischen einer Terminal-Session und einem System-Prozess zum Tragen (mit der Entscheidung für einen Abbruch oder nicht, ansonsten bräuchte es keine Software wie "screen" o.ä.) ... jedenfalls nach meinem begrenzten Verständnis. Und spätestens bei der Frage, wie das festgestellt wird, könnte dann tatsächlich die Ursache für das unterschiedliche Verhalten (Abbruch bei Start aus rc.custom, wo der zusätzliche "sh"-Start aus der rc.mod "dazwischen" ist und der Fortsetzung beim Sourcen in der rc.tail.sh, wie ich es zum Test auf der 7490 gemacht habe) liegen.
Der "normale" Vorgang beim Starten eines neuen Prozesses ist ja ein fork(), gefolgt von einem exec() (die verschiedenen exec-Varianten fassen wir mal so zusammen, funktionell sollte es nach meinen Begriffen keinen Unterschied machen), wo dann das eigentlich auszuführende File geladen wird. Damit unterscheidet sich aber ein solcher Child-Prozess erst einmal nicht von mehreren Threads innerhalb eines Prozesses (wir lassen clone() vs. fork() im MT-Kontext mal bitte außer Acht) und muß erst irgendwie über entsprechende Syscalls "separiert" werden, wenn es ein "wirklich unabhängiger Prozess" werden soll. Ist da exec() wirklich schon der alles entscheidende Faktor? Nach meinem Verständnis ändert das exec() alleine weder die Prozess-Hierarchie noch die "personality" (solange das neue Image kein setuid/setgid-Flag mitbringt). Die "disassociation from the controlling terminal" kann m.E. nur über eine andere Session-ID erfolgen.
Wie das bei einem "richtigen" System mit bash und glibc läuft, ist mir schon klar ... da sind aber einige Unterschiede/Widersprüche für mich in der Reaktion des Embedded-Systems der FRITZ!Box und die basieren - immer nur nach meinem Verständnis, das betone ich gerne noch einmal - auf der Busybox und der uClibc (wenn nicht eine andere Verarbeitung im Kernel erfolgt) - wenn nicht, wo liegen sie dann?
Ich will/werde trotzdem kein Freetz auf 7490 aufsetzen, um das anhand der rc.custom selbst zu testen. Es geht mir persönlich eher nicht nahe, ich bin nur neugierig, was am Ende die Ursache ist. Ich suche also keinen Workaround oder eine funktionierende Lösung, ich suche - eben aus reinem Interesse - eine schlüssige Erklärung für unterschiedliches Verhalten. Thesen dazu kann ich mir genug vorstellen, die tatsächliche Ursache ist das Ziel der Erkenntnis.
Das ist jetzt sicherlich etwas spezieller geworden (damit schwerer zu lesen), als ich es ursprünglich vorhatte und nicht mehr direkt im Zusammenhang mit dem Thema "rc.custom & Beenden von Childs" ... aber ich wurde "gezwungen".
