Warum müssen wir den Dateinamen in exec-Funktionen zweimal übergeben?

10

Ich las die erweiterte Programmierung in der UNIX - Umgebung von Stevens, 8 th Kapiteln. Ich lese und verstehe alle sechs Exec-Funktionen.

Eine Sache, die mir auffällt, ist in allen Exec-Funktionen:

  • Das erste Argument ist der Dateiname / Pfadname (abhängig von der Exec-Funktion).
  • Das zweite Argument ist argv [0], das wir eingeben. Dies main()ist der Dateiname selbst.

Hier müssen wir also den Dateinamen in der Funktion zweimal übergeben.

Gibt es einen Grund dafür (als ob wir den Dateinamen nicht aus dem Pfadnamen des ersten Arguments erhalten könnten)?

munjal007
quelle

Antworten:

14

Hier müssen wir also den Dateinamen in der Funktion zweimal übergeben.

Sie sind nicht ganz dasselbe, wie Sie feststellen, wenn Sie beobachten, dass einer von ihnen als argv[0]Wert verwendet wird. Dies muss nicht mit dem Basisnamen der ausführbaren Datei identisch sein. Viele / die meisten Dinge ignorieren es und Sie können alles hineinstecken, was Sie wollen.

Der erste ist der tatsächliche Pfad zur ausführbaren Datei, für die eine offensichtliche Notwendigkeit besteht. Der zweite wird angeblich als der Name, der zum Aufrufen verwendet wird, an den Prozess übergeben, aber z.

execl("/bin/ls", "banana", "-l", NULL);

Funktioniert gut, vorausgesetzt, es /bin/lsist der richtige Weg.

Einige Anwendungen verwenden jedoch argv[0]. Normalerweise haben diese einen oder mehrere Symlinks $PATH; Dies ist bei Komprimierungsprogrammen üblich (manchmal verwenden sie stattdessen Shell-Wrapper). Wenn Sie xzinstalliert ist , stat $(which xzcat)zeigt es ist ein Link zu xz, und man xzcatist die gleiche wie man xzdie erklärt , „xzcat äquivalent zu xz --decompress --stdout“. Die Art und Weise, wie xz erkennen kann, wie es aufgerufen wurde, besteht darin, Folgendes zu überprüfen argv[0]:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);
Goldlöckchen
quelle
4
Ah, das würde also erklären, wie es sein busyboxkann, was Sie wollen, je nachdem, wie Sie es richtig nennen?
Terdon
3
@terdon Genau so erfüllt die einzelne Binärdatei für Busybox so viele verschiedene Befehle.
Mah
7
Was bedeuten würde, dass /bin/lseine Busybox nicht wissen würde, wie sie ausgeführt werden soll banana!
Riking
6

Sie müssen den Dateinamen nicht zweimal übergeben.

Die erste ist die Datei, die tatsächlich ausgeführt wird.

Das zweite Argument ist argv[0], was der Prozess sein sollte, dh was der Prozess als seinen Namen sehen sollte. Wenn Sie beispielsweise lsvon der Shell aus laufen , ist das erste Argument /bin/lsdas zweite ls.

Sie können eine bestimmte Datei ausführen und über das zweite Argument etwas anderes aufrufen. Das Programm kann seinen Namen überprüfen und sich je nach Name unterschiedlich verhalten. Dies kann auch über feste Links (oder symbolische Links) erfolgen, bietet jedoch mehr Flexibilität.

wurtel
quelle
In der Tat sind Links die gleiche Methode, da diese argv[0]auf den Linknamen gesetzt wird.
Goldlöckchen
Im letzten Absatz "Sie können eine bestimmte Datei ausführen und über das zweite Argument etwas anderes aufrufen; das Programm kann ihren Namen überprüfen und sich je nach Name 'anders' verhalten". Kannst du bitte näher darauf eingehen oder mir ein paar Lesungen geben, ich bin neu in dieser Umgebung.
Munjal007
Der letzte Teil der Antwort von Goldlöckchen erklärt dies.
Wurtel
1

Das Mitnehmen ist, dass argv[0]auf alles (einschließlich NULL) eingestellt werden kann. Vereinbarungsgemäß , argv[0]wird auf den Pfad gesetzt werden die ausführbare Datei gestartet wurde als (von der Shell - Prozess , wenn es funktioniert das execve()).

Wenn ./foound dir/barzwei verschiedene Verbindungen (hart oder symbolische) zu derselben ausführbar sind, ausgehend dann das Programm von der Schale der beiden Pfade verwendeten , wird eingestellt argv[0]auf ./foound dir/barist.

Die Tatsache, die sein argv[0]kann, NULLwird oft übersehen. Der folgende Code könnte NULL argv[0]beispielsweise für a abstürzen (obwohl glibc stattdessen etwas wie <null> druckt argv[0]):

if (argc != 3) {
    fprintf(stderr, "%s: expected 2 arguments\n", argv[0]);
    exit(EXIT_FAILURE);
}

Eine Alternative unter Linux ist die Verwendung /proc/self/exein solchen Fällen.

Ulfalizer
quelle
Wie können Sie argv [0] auf ./foo und dir / bar setzen
munjal007
@ munjal007 Es tut mir leid, wenn ich unklar war. Ich wollte das Programm zweimal ausführen: einmal als ./foound einmal als dir/bar. argv[0]wird für diese beiden Fälle unterschiedlich sein (in jedem Fall ist es der gleiche wie der Pfad, den Sie verwendet haben).
Ulfalizer
@ munjal007 Das setzt natürlich voraus, dass Sie es von der Shell aus ausführen. Der Punkt ist, dass Sie argv[0]alles einstellen können, wenn Sie exec*()das Programm selbst. Es ist eine Konvention der Shell, argv[0]den Pfad festzulegen, der zum Starten des Programms verwendet wurde (und es ist ratsam, dasselbe zu tun, wenn Sie exec*()ein Programm erstellen, da viele Programme argv[0]den Pfad überprüfen und erwarten, dass er den Pfad enthält).
Ulfalizer