Unterschied zwischen "System" und "Exec" unter Linux?

72

Was ist der Unterschied zwischen systemund execFamilienbefehlen? Besonders möchte ich wissen, welcher von ihnen einen untergeordneten Prozess zum Arbeiten erstellt?

Kamil
quelle

Antworten:

95

system()ruft dazu auf sh, Ihre Befehlszeile zu verwalten, damit Sie Platzhaltererweiterungen usw. erhalten können, exec()und seine Freunde ersetzen das aktuelle Prozessabbild durch ein neues Prozessabbild.

Mit system()läuft Ihr Programm weiter und Sie erhalten einen Status über den von Ihnen aufgerufenen externen Befehl zurück. Mit exec()wird Ihr Prozess ausgelöscht.

Im Allgemeinen könnte man sich system()eine übergeordnete Schnittstelle vorstellen. Sie könnten ihre Funktionalität selbst mit einer Kombination duplizieren fork(), exec()und wait().

Um Ihre letzte Frage zu beantworten, system()wird ein untergeordneter Prozess erstellt, die exec()Familie jedoch nicht. Sie müssten dafür verwenden fork().

Carl Norum
quelle
Der systemAufruf erzeugt eine neue Shell, um auch den angegebenen Befehl auszuführen, oder führt er den Befehl in derselben Shell aus.
Krishna Oza
@Krishna_Oza - Es gibt keine "gleiche Shell", es sei denn, der Programmaufruf system()selbst ist eine Shell. Ich bin mir nicht sicher, ob ich folge. In meiner Dokumentation hier heißt es: "Die system()Funktion übergibt den Argumentbefehl an den Befehlsinterpreter sh(1)."
Carl Norum
3
Zitat aus dem systemPOSIX-Handbuch : Die system()Funktion soll sich so verhalten, als ob ein untergeordneter Prozess mit erstellt wurde fork(), und der untergeordnete Prozess hat das Dienstprogramm sh execl()wie folgt aufgerufen :execl(<shell path>, "sh", "-c", command, (char *)0); .
patryk.beza
20

Die exec-Funktion ersetzt das aktuell ausgeführte Prozessabbild, wenn erfolgreich, es wird kein untergeordnetes Element erstellt (es sei denn, Sie tun dies zuvor selbst mit fork()). Die Funktion system () gibt einen untergeordneten Prozess aus und kehrt zurück, wenn die Ausführung des Befehls abgeschlossen ist oder ein Fehler auftritt.

BobbyShaftoe
quelle
7

system()führt den angegebenen Befehl in einem untergeordneten Prozess aus, den er erzeugt. exec()ersetzt den aktuellen Prozess durch den Aufruf der neuen ausführbaren Datei, die Sie angeben. Wenn Sie einen untergeordneten Prozess mit erzeugen möchten, execmüssen fork()Sie diesen Prozess im Voraus ausführen.

Timo Geusch
quelle
6

So erstellen Sie einen Prozess:

  • fork(2), ein Systemaufruf direkt an den Kernel

So führen Sie ein Programm aus: Ersetzen Sie das aktuelle Image:

  • execve(2), ein Systemaufruf direkt an den Kernel, normalerweise nur aufgerufen exec

So warten Sie, bis ein untergeordneter Prozess abgeschlossen ist:

  • wait(2), ein Systemaufruf direkt an den Kernel

So führen Sie ein Programm in einer Shell in einem untergeordneten Prozess aus und warten, bis es abgeschlossen ist:

  • system(3), eine Bibliotheksfunktion

So erhalten Sie die Manpages für alle oben genannten Punkte:

   $ man 2 fork execve wait
   $ man 3 system
DigitalRoss
quelle
2

system () ruft die Standardbefehlsshell Ihres Systems auf, die die als Argument übergebene Befehlszeichenfolge ausführt, die selbst weitere Prozesse erstellen kann oder nicht, die vom Befehl und vom System abhängen. In beiden Fällen wird mindestens ein Befehls-Shell-Prozess erstellt.

Mit system () können Sie jeden Befehl aufrufen, während Sie mit exec () nur eine ausführbare Datei aufrufen können. Shell-Skripte und Batch-Dateien müssen von der Befehlsshell ausgeführt werden.

Grundsätzlich sind sie völlig unterschiedlich und werden für unterschiedliche Zwecke verwendet. Außerdem ersetzt exec () den aufrufenden Prozess und kehrt nicht zurück. Ein nützlicherer Vergleich wäre zwischen system () und spawn (). Während das System möglicherweise einfacher aufzurufen ist, gibt es einen Wert zurück, der angibt, ob die Befehlsshell aufgerufen wurde, und nichts über den Erfolg des Befehls selbst aussagt. Mit spawn () können Sie den Exit-Code des Prozesses abrufen. Konventionell wird ein Wert ungleich Null verwendet, um Fehlerbedingungen anzuzeigen. Wie exec () muss spawn () eine ausführbare Datei aufrufen, kein Shell-Skript oder einen integrierten Befehl.

Clifford
quelle
2

int system(const char *cmdstring);

Ex: system("date > file");


Im Allgemeinen wird das System durch Aufrufen von fork, exec und waitpid implementiert . Es gibt drei Arten von Rückgabewerten.

  • Wenn entweder die Verzweigung ausfällt oder waitpid einen anderen Fehler als EINTR zurückgibt, gibt das System –1 zurück, wobei errno gesetzt ist, um den Fehler anzuzeigen.
  • Wenn die Ausführung fehlschlägt, was bedeutet, dass die Shell nicht ausgeführt werden kann, ist der Rückgabewert so, als hätte die Shell exit (127) ausgeführt.
  • Andernfalls sind alle drei Funktionen - Fork, Exec und Waitpid - erfolgreich, und der Rückgabewert vom System ist der Beendigungsstatus der Shell in dem für Waitpid angegebenen Format.

Die Fork- Funktion besteht darin, einen neuen Prozess (das untergeordnete Element ) zu erstellen, der dann durch Aufrufen einer der exec- Funktionen die Ausführung eines anderen Programms bewirkt . Wenn ein Prozess eine der exec-Funktionen aufruft, wird dieser Prozess vollständig durch das neue Programm ersetzt, und das neue Programm wird an seiner Hauptfunktion ausgeführt. Die Prozess-ID ändert sich in einem Exec nicht, da kein neuer Prozess erstellt wird. exec ersetzt lediglich den aktuellen Prozess - seine Text-, Daten-, Heap- und Stapelsegmente - durch ein brandneues Programm von der Festplatte.

Es gibt sechs verschiedene Ausführungsfunktionen :


int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );

int execv(const char *pathname, char *const argv []);

int execle(const char *pathname, const char *arg0, .../* (char *)0, char *const envp[] */ );

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

int execlp(const char *filename, const char *arg0,... /* (char *)0 */ );

int execvp(const char *filename, char *const argv []);

Madhavan G.
quelle
1

exec () ersetzt den aktuell ausgeführten Prozess durch das Prozessabbild der ausgeführten Funktion. Damit können nur ausführbare Dateien aufgerufen werden.

system () gibt einen neuen Prozess implizit ab, um die Anforderung zu bearbeiten, und gibt den Wert zurück, den es durch den untergeordneten Prozess erhalten hat, den es ursprünglich gabelte. Es verwendet die Standard-Shell des Systems, um die Operation auszuführen.

Morpheus
quelle
1

Es gibt einige signifikante Unterschiede zwischen exec(2)und system(3)das sollte beachtet werden. system()kehrt zum Aufrufer zurück, während exec()der vorhandene Code durch das neue Bild ersetzt wird. Dies wurde oben erklärt.

Der nicht so subtile Unterschied tritt jedoch auf, wenn Sie eine Prozedur ausführen und dann zu Ihrem vorhandenen Code zurückkehren möchten, um den Rückkehrcode von der aufgerufenen Prozedur zu erhalten. system()stellt einen Rückkehrcode bereit, aber der Rückkehrcode kann nur zum Erkennen eines Fehlerzustands und nicht zum Wiederherstellen eines Rückkehrcodes verwendet werden.

Eine mögliche richtige Folge von Systemaufrufen ist:

#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2

int main (int argc, char *argv[])
{
  pid_t child_pid, wait_pid;
  int * child_status;
  char * exec_path = "/path/to/executable";
  char * child_args[NUMARGS] = {0,0};

  child_pid = fork();
  if (0 == child_pid)
  { // In child process
     ...
     int child_ret_code = execv(exec_path, child_args);  //or whichever flavor of exec() that floats your boat
     ... // if child_ret_code = -1, process execv() error return
  }
  else if (-1 == child_pid)
  {
     ... //process error return from fork
  }
  else if (0 < child_pid)
  {  // Parent process
     wait_pid = wait(child_status);
     if (-1 == wait_pid)
     {
       ... //Process error return from wait()
     }
     else
     {  //  Good fork/exec/wait
        if (WIFEXITED(child_status))  // Child exited normally and hopefully returned exit code
        {
           int child_ret_code = WEXITSTATUS(child_status);
           ...  // Continue on as you would after call to system(3)
                //   except now you have the return code you needed
        }
     }
  }
}

Es gibt andere Feinheiten in dieser Sequenz, die durch sorgfältiges Lesen der relevanten Manpages ermittelt werden können. Dieser Code funktioniert jedoch einwandfrei, wenn keine Signale, mehrere untergeordnete Prozesse usw. vorhanden sind. Außerdem können die Inline-Deklarationen den Umfang der Variablen, die jedoch enthalten sind, damit dieser Code als funktionierende Vorlage verwendet werden kann (Sie können einen anderen Codierungsstil verwenden :-).

Jon Spencer
quelle
1

Die Antwort von JonSpencer ist in Ordnung, außer dass child_status ein int (kein Zeiger auf int) sein muss und als Referenz an die Wartefunktion übergeben werden muss.

Der Code wäre also hauptsächlich derselbe, nur um diese paar Dinge zu ändern:

#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2

int main (int argc, char *argv[])
{
  pid_t child_pid, wait_pid;
  int child_status;
  char * exec_path = "/path/to/executable";
  char * child_args[NUMARGS] = {0,0};

  child_pid = fork();
  if (0 == child_pid)
  { // In child process
     ...
     int child_ret_code = execv(exec_path, child_args);  //or whichever flavor of exec() that floats your boat
     ... // if child_ret_code = -1, process execv() error return
  }
  else if (-1 == child_pid)
  {
     ... //process error return from fork
  }
  else if (0 < child_pid)
  {  // Parent process
     wait_pid = wait(&child_status);
     if (-1 == wait_pid)
     {
       ... //Process error return from wait()
     }
     else
     {  //  Good fork/exec/wait
        if (WIFEXITED(child_status))  // Child exited normally and hopefully returned exit code
        {
           int child_ret_code = WEXITSTATUS(child_status);
           ...  // Continue on as you would after call to system(3)
                //   except now you have the return code you needed
        }
     }
  }
}

(Weisen Sie darauf hin, dass ich noch nicht genug Ruf habe, um Jons Beitrag zu kommentieren, also habe ich ihn bearbeitet. Einige Leute lehnten die Ausgabe ab und baten mich, die Frage zu beantworten, anstatt sie zu bearbeiten, aber ich denke, dass dies in diesem Fall viel einfacher und praktischer ist und klar, einen vorhandenen Code zu bearbeiten, der nur einen kleinen Fehler korrigiert, als eine vollständige Antwort zum Kopieren / Einfügen / Ändern zu schreiben.) Wie auch immer, danke JonSpencer für Ihre Antwort, es war wirklich nützlich für mich!

jap jap
quelle
0

System () erstellt einen untergeordneten Prozess und ruft eine andere Unter-Shell auf, während exec () keinen untergeordneten Prozess erstellt. Das angegebene Beispiel löscht den Unterschied.

etwas Code ...

exec ('ls -l')

echo "1 2 3" // Dies wird nicht in bash ausgeführt (als exec-Befehl wird dieselbe Shell verwendet)

etwas Code ...

system (ls -l) echo "1 2 3" // Dies wird nach Abschluss des untergeordneten Systemprozesses ausgeführt, da sie sich von der übergeordneten PID unterscheiden.

Poseidon_Geek
quelle
0

system () ruft das gewünschte Programm oder den integrierten Befehl mithilfe einer Shell auf. Dies ist eine ineffiziente Methode, da eine Shell vor dem Start des Programms gestartet wird.

Bei der exec-Familie von Systemaufrufen wird ein ganz neues Image erstellt, dh sie ersetzen den aktuellen Prozess durch einen neuen Prozess, der durch den Pfad oder die Datei oder das von Ihnen erwähnte Argument angegeben wird.

Beachten Sie, dass bei Verwendung der exec-Familie von Systemaufrufen das ursprüngliche Programm nach dem Start des neuen Programms nicht mehr ausgeführt wird.

Kishanp
quelle
0

Im Allgemeinen ist "System" so ineffizient und Sie sollten es nur verwenden, wenn Sie einen kleinen Code haben. Wenn Sie in Ihrem Prozess mehrere Programme ausführen müssen, sollten Sie fork & exec verwenden, obwohl Sie dies komplizierter machen. Hier ist eine Liste der Unterschiede zwischen ihnen:

1- Der Befehl "system" erstellt eine Kopie der Shell, um Ihr Programm auszuführen. Jedes Mal, wenn Sie ein System aufrufen, erstellen Sie eine Kopie der Shell. Verwenden Sie es also nicht, wenn Sie viele Programme in Ihrem Prozess ausführen müssen.

2- Insbesondere wenn Sie Systemfunktionen wie "mv", "mkdir" ausführen möchten, ist es besser, Routinen wie mkdir (), unlink () oder remove () zu verwenden, anstatt sie über "system (" auszuführen) rm .... ") oder system (" mkdir .... ")".

3- Da das System die Shell aufruft, um das gewünschte Programm auszuführen, können Probleme mit der Benutzerberechtigung auftreten. Beispielsweise kann jemand Ihren Code knacken und etwas anderes ausführen als das Programm, das Sie über den Systembefehl ausführen möchten.

Weitere Informationen finden Sie in Kapitel 11 dieses Buches: "UNIX Systems Programming" von David Curry.

ImanKh
quelle