Wie führt eine Shell ein Programm aus?


11

Wenn ich ein Programm mit gcc kompiliere und versuche, es über die Bash-Shell auszuführen, wie lautet die genaue Reihenfolge der Schritte, gefolgt von Bash, um es auszuführen?

Ich weiß fork(), execve(), loader, dynamic linker(und andere Dinge) beteiligt sind, aber kann jemand eine genaue Abfolge von Schritten und eine geeignete Lesereferenz geben?

Bearbeiten:

Aus den Antworten geht hervor, dass die Frage viele Möglichkeiten implizieren könnte. Ich möchte mich auf einen einfachen Fall beschränken:

(test.c druckt nur Hallo Welt)

$ gcc test.c -o test
$ ./test

Was sind die Schritte im obigen Fall ( ./test), die sich speziell auf das Bash-Startprogramm in einem untergeordneten Prozess beziehen, das Laden, Verknüpfen usw.?


4
Ich lade Sie ein, lwn.net/Articles/630727
cuonglm

3
Warum probieren Sie nicht den Strace Bash-C-Test?
Sergiy Kolodyazhnyy

2
Es scheint, als wäre ein anständiges Lehrbuch für Betriebssysteme eine gute Ressource für das OP. Der Versuch, anhand einzelner Fragen wie dieser zu lernen, wie Betriebssysteme funktionieren, ist wahrscheinlich kein produktiver Prozess.
Barmar

Es wäre nützlich, ein minimales Beispiel einer Shell zu sehen: brennan.io/2015/01/16/write-a-shell-in-c
jinawee

Antworten:


5

Nun, die genaue Reihenfolge kann variieren, da es möglicherweise einen Shell-Alias ​​oder eine Shell-Funktion gibt, die zuerst erweitert / interpretiert wird, bevor das eigentliche Programm ausgeführt wird, und dann Unterschiede zwischen einem qualifizierten Dateinamen ( /usr/libexec/foo) und etwas, das in allen Verzeichnissen gesucht wird der PATHUmgebungsvariablen (nur foo). Auch können die Einzelheiten der Durchführung Sache noch komplizierter, da foo | bar | zotmehr Arbeit für die Shell erfordert ( eine bestimmte Anzahl von fork(2), dup(2)und, natürlich, pipe(2)unter anderem Systemaufrufe), während wie etwas exec fooist viel weniger Arbeit als die Schale nur ersetzt selbst mit das neue Programm (dh es tut es nicht fork). Wichtig sind auch Prozessgruppen (insbesondere die Vordergrundprozessgruppe, von der alle PIDs essenSIGINTWenn jemand anfängt, auf Ctrl+ zu pürieren C, werden Sitzungen und ob der Job im Hintergrund ausgeführt wird, überwacht ( foo &) oder im Hintergrund, ignoriert ( foo & disown). E / A-Umleitungsdetails ändern auch Dinge, z. B. wenn die Standardeingabe von der Shell ( foo <&-) geschlossen wird oder wenn eine Datei als stdin ( foo < blah) geöffnet wird .

straceoder ähnliches ist informativ über die spezifischen Systemaufrufe, die während dieses Prozesses ausgeführt werden, und es sollte Manpages für jeden dieser Aufrufe geben. Geeignetes Lesen auf Systemebene wäre eine beliebige Anzahl von Kapiteln aus Stevens '"Advanced Programming in the UNIX Environment", während ein Shell-Buch (z. B. "From Bash to Z Shell") die Shell-Seite der Dinge detaillierter behandelt.


Ich habe die Frage bearbeitet, um sie auf einen einfachen Fall zu beschränken
Jake

1

Angenommen, eine Lehrbuchbeispiel-Shell (aus Gründen der Codeklarheit) wird bereits ausgeführt (damit der dynamische Linker fertig ist). Für die von Ihnen erwähnten Befehle muss die Shell die folgenden Systemaufrufe ausführen:

  • read: ruft in diesem Fall den nächsten Befehl ab gcc
  • Gabel: Es werden zwei Prozesse benötigt. Wir gehen davon aus, dass der Elternteil PID 500 hat und das Kind zur Veranschaulichung.
  • Der Elternteil ruft wait (501) auf, während das Kind exec aufruft. Zu diesem Zeitpunkt läuft die Shell nicht mehr auf pid 501. gcc führt viele Systemaufrufe durch, einschließlich mindestens Öffnen, Schließen, Lesen, Schreiben, chmod, fork, exec, wait und exit.
  • Wenn gcc-Aufrufe beendet werden, wird wait zurückgegeben, write wird aufgerufen, um die Eingabeaufforderung anzuzeigen, und der Vorgang wird wiederholt.

Kompliziertere Befehle machen diese Grundsequenz natürlich komplizierter. Zwei einfachere Beispiele für grundlegende Komplikationen sind die grundlegende Umleitung, bei der eine offene, geschlossene Dup-Sequenz zwischen dem Fork und den Exec- und Hintergrundprozessen eingefügt wird, bei denen das Warten übersprungen wird (und ein weiteres Warten einem Sigchld-Handler hinzugefügt wird).


Kleiner Zusatz: Die Frage fragt nach Laden und dynamischer Verknüpfung. Der gesamte Code, der statisch verknüpft ist, dh tatsächlich in der Programmdatei enthalten ist, wird vom Kernel ausgeführt, bevor das Programm gestartet wird. Dynamisch geladene Bibliotheken, dh separate Dateien, werden vom Programm selbst verwaltet, bevor main () gestartet wird. Der Code hierfür wird automatisch von gcc hinzugefügt.
Stig Hemmer

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.