Verwendung von Execve in C

Abdul Mateen 12 Oktober 2023
  1. der Systemaufruf exec in C
  2. der Systemaufruf execve in C
Verwendung von Execve in C

In diesem Tutorial wird die Verwendung von execve besprochen, um sowohl Linux-Standardbefehle als auch unsere ausführbaren Dateien in C auszuführen.

Zuerst werden wir den Systemaufruf exec und die Familie des exec besprechen. Als nächstes werden wir unsere ausführbaren Dateien in C aufrufen, und schließlich werden wir sowohl Standard-Linux-Befehle als auch unsere ausführbaren Dateien besprechen.

der Systemaufruf exec in C

Der Systemaufruf exec ersetzt den laufenden Prozess durch einen anderen ausführbaren Prozess. Der Adressraum des laufenden Prozesses wird durch den Adressraum des neuen Prozesses ersetzt.

Es ist wichtig zu beachten, dass das neue Programm in denselben Adressraum geladen wird. Die Prozess-ID bleibt gleich.

Das neue Programm läuft unabhängig; Das heißt, der Startpunkt ist der Einstiegspunkt des neuen Programms. Der Systemaufruf exec hat viele Varianten.

  1. execl
  2. execle
  3. execlp
  4. execv
  5. execve
  6. execvp

Diese Funktionen verwenden dieselbe Basis exec, gefolgt von einem oder mehreren Buchstaben. Das Detail der zusätzlichen Buchstaben ist unten.

  1. e - Hier steht e für Umgebungsvariablen; Diese Funktion hat ein Array von Zeigern, die auf Umgebungsvariablen zeigen. Die Liste der Umgebungsvariablen wird explizit an das neu geladene Programm übergeben.
  2. l – Hier steht l für Kommandozeilenargumente. Wir können der Funktion die Liste der Befehlszeilenargumente übergeben.
  3. p - Hier steht p für den Umgebungsvariablenpfad. In dieser Funktion hilft die Pfadvariable, die Datei zu finden, die als Argument an den neu geladenen Prozess übergeben wird.
  4. v - Hier steht v auch für die Kommandozeilenargumente. In dieser Funktion werden jedoch Befehlszeilenargumente als ein Array von Zeigern an den neu geladenen Prozess übergeben.

Beim grundlegenden Systemaufruf exec wird der Adressraum des aktuellen Prozesses durch den Adressraum des neu geladenen Programms ersetzt; als Ergebnis wird der aktuell laufende Prozess beendet. Der frisch geladene Prozess wird diesem Systemaufruf als Argument übergeben.

Der neu geladene Prozess hat dieselbe Prozess-ID, dieselben Umgebungsvariablen und denselben Satz von Dateideskriptoren. Allerdings sind die CPU-Statistiken und der virtuelle Speicher betroffen.

Syntax von exec-Aufrufen

Hier haben wir die Syntax von sechs Systemaufrufen, Varianten grundlegender exec-Systemaufrufe.

int execl(const char* path, const char* arg, ...) int execle(
    const char* path, const char* arg, ...,
    char* const envp
        []) int execlp(const char* file, const char* arg,
                       ...) int execv(const char* path,
                                      const char* argv
                                          []) int execve(const char* path,
                                                         const char* argv[],
                                                         char* const envp
                                                             []) int execvp(const char*
                                                                                file,
                                                                            const char* argv
                                                                                []) int execvpe(const char*
                                                                                                    file,
                                                                                                const char* argv
                                                                                                    [],
                                                                                                char* const envp
                                                                                                    [])

Erstens ist der Rückgabetyp aller Funktionen int. Im Falle einer erfolgreichen Operation (dh das neue Programm wird geladen und ersetzt) wird jedoch nichts zurückgegeben, da das aktuelle Programm nicht mehr vorhanden ist, um den Rückgabewert zu erhalten.

Bei einem Fehlschlag aufgrund eines Fehlers wird das neue Programm nicht geladen und -1 wird an das bestehende Programm zurückgegeben.

Im ersten Argument gibt es einen Unterschied zwischen Pfad und Datei. Das p, execlp, execvp und execvpe haben eine Datei anstelle des Pfads.

Der path gibt den vollständigen Pfad der auszuführenden/zu ladenden Datei an. Die file gibt den Pfadnamen an, der hilft, die Datei des neuen Programms zu finden.

Beim zweiten Argument besteht der Unterschied darin, dass Funktionen mit v ein zweidimensionales Array vom Typ char mit mehreren Strings (inklusive Dateinamen) haben.

Im Gegensatz dazu haben andere Prozeduren ein oder mehrere eindimensionale Arrays vom Typ char, wobei das erste Element dieser Liste den file-Namen enthält, das zweite Element einige Parameter enthalten kann usw.

Schließlich hat bei Funktionen mit e ein dritter/letzter Parameter eine Umgebungsvariable als Array von Zeigern.

Codierungsbeispiel für Systemaufruf exec

Es ist besser, vor der weiteren Diskussion ein Beispiel zu sehen. In diesem Beispiel verwenden wir den Quellcode des Programms als Eingabe für das Programm.

Hier haben wir dieses Programm (mit dem Namen execl0.c) im Verzeichnis des ausführbaren Codes gespeichert. Das bedeutet, dass sowohl der Quellcode als auch der ausführbare Code im selben Verzeichnis vorhanden sind.

#include <stdio.h>
#include <unistd.h>
int main(void) {
  char binaryPath[] = "/bin/wc";
  char arg1[] = "wc";
  char arg2[] = "-w";
  char arg3[] = "execl0.c";
  printf("First line of current program\n");
  execl(binaryPath, arg1, arg2, arg3, NULL);
  printf("Last line of current program\n");
  return 1;
}

Der obige Code verwendet einen execl-Systemaufruf, der nur wenige einfache Variablen vom Typ char* hat. Die erste Variable enthält den Pfad und Namen des neuen Programms (das ausgeführt werden soll), und die zweite Variable hat einen Parameter wc (wiederum den Namen des Programms).

Die dritte Variable hat einen Parameter -w, um den Befehl als wc -w auszuführen, um Wörter in der Quelldatei zu zählen.

Beachten Sie auch zwei zusätzliche print-Anweisungen, erstens vor dem Systemaufruf und zweitens am Ende des Programms.

Ausgang:

First line of current program
32 execl0.c

Die Ausgabe zeigt, dass unser neues Programm erfolgreich geladen und ausgeführt wurde. Beachten Sie jedoch, dass die erste print-Anweisung ausgeführt wird (siehe die erste Zeile der Ausgabe ('First line of current program').

Die letzte print-Anweisung wird nicht ausgeführt, da das aktuelle Programm automatisch beendet wird, wenn ein neues Programm erfolgreich geladen wurde.

Die zweite Ausgabezeile zeigt die Wortzahl in der Datei execl0.c.

der Systemaufruf execve in C

Nun werden wir den execve-Aufruf im Detail besprechen.

Syntax:

int execve(const char* path, const char* argv[], char* const envp[])

Hier ist das erste Argument der path; Wie bereits erwähnt, hilft die Umgebungsvariable path dabei, das auszuführende Programm als neues Programm zu finden.

Das zweite Argument ist ein zweidimensionales Array von Zeichen oder ein eindimensionales Array von Zeichenfolgen mit einer Liste von Befehlszeilenargumenten.

Das dritte Argument ist wiederum ein zweidimensionales Zeichenfeld oder ein eindimensionales Zeichenkettenfeld mit einer Liste von Umgebungsvariablen.

In der Familie exec ist execve ein überzeugender Befehl mit drei Argumenten path, einer Liste von Befehlszeilenargumenten und einer Liste von Umgebungsvariablen. Sehen wir uns einen Code an, um den Befehl echo aus dem Programm auszuführen.

#include <stdio.h>
#include <unistd.h>
int main(void) {
  char *binaryPath = "/bin/bash";
  char *args[] = {binaryPath, "-c",
                  "echo visit $HOSTNAME:Fun with your browser", "", NULL};
  char *const env[] = {"HOSTNAME=www.delftstack.com", "port=8080", NULL};
  execve(binaryPath, args, env);
  return 1;
}

In der ersten Zeile der Primärfunktion ist /bin/bash der Pfad, in dem der Befehl existiert. In der zweiten Zeile enthält die Liste der Kommandozeilenargumente drei Parameter vor NULL, der das Argument beendet.

Auch hier ist das erste Argument der Pfad und der zweite Parameter -c steht für cmd, was die Übergabe von Code als String erlaubt.

Der dritte Parameter ist der Befehl; wie im code ist echo der befehl.

Die dritte Zeile des Codes enthält zwei Strings mit zwei Umgebungsvariablen, HOSTNAME und port. Schließlich ist die Ausgabe des Codes:

visit www.delftstack.com : Fun with your browser

In diesem Code haben wir einen Linux-Befehl aus unserem Programm ausgeführt. Als nächstes führen wir ein externes ausführbares Programm innerhalb des aktuellen Programms aus.

Sehen Sie sich zunächst dieses Programm an:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *args[]) {
  int i;
  int count = atoi(args[1]);
  for (i = 1; i <= count; i++) printf("[%d]", i);
  printf("\n");
  return 0;
}

Dieses Programm akzeptiert Befehlszeilenargumente. Das Kommandozeilenargument (als String übergeben) wird in der zweiten Zeile der Funktion main in eine ganze Zahl umgewandelt.

Als nächstes führen wir eine Schleife von eins bis count aus und geben die Zählung in eckigen Klammern aus. Sehen Sie sich die Ausgabe dieses Codes an.

$./ test 4 [1][2][3][4]

Wir haben die ausführbare Datei mit dem Namen test erstellt. Wir haben die test-Datei mit dem Parameter 4 von einer Eingabeaufforderung ausgeführt.

Wir können in der Ausgabe in eckigen Klammern das Zählen von eins bis vier sehen.

Als nächstes müssen wir dieses Programm test als externen Befehl von einem anderen Programm ausführen. Dazu müssen wir den Pfad des ausführbaren Programms test angeben.

Hier lautet der vollständige Pfad /home/mateen/Documents/test. Daher werden wir diesen Pfad in unserem nächsten Programm angeben, um die ausführbare Datei zu finden.

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *ar[]) {
  printf("This is the first line\n");
  char *binaryPath = "/bin/bash";
  char name[80] = "/home/mateen/Documents/test ";
  strcat(name, ar[1]);
  char *args[] = {binaryPath, "-c", name, NULL};
  char *env_args[] = {"/bin/bash", (char *)0};
  execve(binaryPath, args, env_args);
  printf("This is the last line\n");
  return 1;
}

Wir haben eine weitere Bibliothek hinzugefügt, um die Funktion zum Verketten von Zeichenfolgen zu verwenden. In der dritten Zeile der Funktion main haben wir einen vollständigen Pfad und Dateinamen, da dies kein Linux-Befehl ist; stattdessen ist dies ein benutzerdefiniertes ausführbares Programm (bereits ausführlich besprochen).

In der folgenden Zeile verketten wir das an unser aktuelle Programm übergebene Befehlszeilenargument mit dem Namen des neuen Programms. Auch in der fünften Zeile haben wir Kommandozeilenargumente mit einem Pfad, -c.

Der dritte Parameter ist der Variablenname mit Pfad + Name der ausführbaren Datei + Argument, das an das aktuelle Programm übergeben wird.

Ausgang:

$ ./a.out 5
This is the first line
[1][2][3][4][5]

Wir führen unser aktuelles Programm mit dem Kommandozeilenparameter 5 aus. Die erste Ausgabezeile enthält die erste print-Anweisung.

Als nächstes können Sie sehen, dass unser Testprogramm ausgeführt wird. Die Zählweise von 1 bis 5 wird in eckige Klammern geschrieben.

Das Fazit ist schließlich, dass wir mit execve sowohl Linux-Befehle als auch unsere ausführbaren Programme ausführen können. Im Fall des Linux-Befehls können wir den Pfad übergeben, um das Linux-Programm zu finden.

Im Fall einer anderen/externen ausführbaren Datei können wir einen vollständigen Pfad mit dem Dateinamen angeben; In diesem Fall wird das Programm automatisch im angegebenen Pfad abgelegt. In diesem Fall ignoriert der Befehl die path-Variable in der dritten Zeile der main-Funktion.