web-dev-qa-db-de.com

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

Was ist der Unterschied zwischen den Befehlen system und exec family? Insbesondere möchte ich wissen, welcher von ihnen einen untergeordneten Prozess zur Arbeit erzeugt?

63
Kamil

system() ruft sh auf, um Ihre Befehlszeile zu bearbeiten, 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 system() als übergeordnetes Interface vorstellen. Sie können die Funktionalität selbst mit einer Kombination der Funktionen fork(), exec() und wait() duplizieren.

Um Ihre letzte Frage zu beantworten, bewirkt system(), dass ein untergeordneter Prozess erstellt wird, und die exec()-Familie nicht. Dafür müssten Sie fork() verwenden.

84
Carl Norum

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

19
BobbyShaftoe

system() führt den bereitgestellten Befehl in einem von ihm erzeugten untergeordneten Prozess aus. exec() ersetzt den aktuellen Prozess durch den Aufruf der von Ihnen angegebenen neuen ausführbaren Datei. Wenn Sie einen untergeordneten Prozess mit exec erzeugen möchten, müssen Sie zuvor den Prozess fork() ausführen.

7
Timo Geusch

So erstellen Sie einen Prozess:

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

Um ein Programm auszuführen, das das aktuelle Bild ersetzt:

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

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 beendet ist:

  • system(3), eine Bibliotheksfunktion

Um die man-Seiten zu erhalten für alle oben genannten:

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

system () ruft den Standardbefehl Shell Ihres Systems auf, der die als Argument übergebene Befehlszeichenfolge ausführt, die wiederum weitere Prozesse erstellen kann oder nicht, die vom Befehl und vom System abhängen. In jedem Fall wird mindestens ein Befehlsshellprozess 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 mit dem Befehl Shell ausgeführt werden.

Grundsätzlich werden sie völlig unterschiedlich 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 der Befehl Shell aufgerufen wurde, und nichts über den Erfolg des Befehls selbst. Mit Spawn () erhalten Sie den Exit-Code des Prozesses. In der Regel wird Nicht-Null verwendet, um Fehlerzustände anzuzeigen. Genau wie exec () muss spawn () eine ausführbare Datei aufrufen, kein Shell-Skript oder einen integrierten Befehl.

2
Clifford

int system(const char *cmdstring);

ZB: system("date > file");


Im Allgemeinen wird system durch Aufruf von fork, exec und waitpid implementiert. Es gibt drei Arten von Rückgabewerten.

  • Wenn entweder die Gabelung fehlschlägt oder waitpid einen anderen Fehler als EINTR zurückgibt, gibt das System –1 mit errno set zurück, um den Fehler anzuzeigen.
  • Wenn der Exec 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.
  • Ansonsten sind alle drei Funktionen (Fork, Exec und Waitpid) erfolgreich, und der Rückgabewert von systemist der Beendigungsstatus der Shell in dem für waitpid angegebenen Format.

Die Fork - Funktion erstellt einen neuen Prozess (das untergeordnete), der dann Die Ausführung eines anderen Programms durch Aufrufen einer der exec - Funktionen bewirkt. Wenn ein Prozess eine der Exec-Funktionen aufruft, wird dieser Prozess vollständig durch das neue Programm ersetzt, und das neue Programm beginnt mit der Ausführung seiner Hauptfunktionat. Die Prozess-ID ändert sich in einem Exec nicht, da kein neuer Prozess erstellt wird. exec ersetzt lediglich den aktuellen Prozess (Text, Daten, Heap und Stack-Segment) durch ein brandneues Programm von disk.

Es gibt sechs verschiedene Exec-Funktionen ,


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 []);

2
Madhavan G

Es gibt einige signifikante Unterschiede zwischen exec(2) und system(3), die beachtet werden sollten. system() kehrt zum Aufrufer zurück, wohingegen exec() den vorhandenen Code durch das neue Bild ersetzt. Dies wurde oben erläutert.

Der nicht so subtile Unterschied besteht jedoch, wenn Sie eine Prozedur ausführen und dann zu Ihrem vorhandenen Code zurückkehren möchten, wobei Sie den Rückkehrcode von der aufgerufenen Prozedur erhalten. system() liefert zwar einen Rückgabecode, der Rückgabecode kann jedoch nur zum Erkennen einer Fehlerbedingung verwendet werden und kann nicht zum Wiederherstellen eines Rückgabecodes verwendet werden.

Eine mögliche Reihenfolge 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 zu dieser Sequenz, die durch ein sorgfältiges Lesen der entsprechenden Manpages bestimmt werden können, aber dieser Code funktioniert gut, wenn keine Signale, mehrere untergeordnete Prozesse usw. vorhanden sind. Auch die Inline-Deklarationen können den Geltungsbereich der Variablen, sind jedoch enthalten, damit dieser Code als funktionierende Vorlage verwendet werden kann (Sie können einen anderen Codierungsstil verwenden :-).

1
Jon Spencer

exec () ersetzt den aktuell laufenden Prozess durch das Prozessabbild der ausgeführten Funktion. Nur ausführbare Dateien können mit diesem Befehl aufgerufen werden.

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

1
Morpheus

system () ruft das gewünschte Programm oder den integrierten Befehl mit einer Shell auf. Dies ist eine ineffiziente Methode, da eine Shell gestartet wird, bevor das Programm gestartet wird. 

Im Falle der Systemaufrufe der exec-Familie wird ein völlig neues Image erstellt, das heißt, der aktuelle Prozess wird durch einen neuen Prozess ersetzt, der durch den Pfad oder die Datei oder durch ein beliebiges Argument angegeben wird.

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

0
kishanp

System () erstellt einen untergeordneten Prozess und ruft eine andere untergeordnete Shell auf, während exec () keinen untergeordneten Prozess erstellt. Im angegebenen Beispiel werden Unterschiede behoben.

etwas code ...

exec ('ls -l')

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

etwas code ...

system (ls -l) echo "1 2 3" // Dies wird ausgeführt, nachdem der untergeordnete Systemprozess abgeschlossen wurde, da sie sich von der übergeordneten PID unterscheiden. 

0
Poseidon_Geek

Im Allgemeinen ist "System" so ineffizient und sollte nicht verwendet werden, es sei denn, Sie haben einen kleinen Code. Wenn Sie mehrere Programme in Ihrem Prozess ausführen müssen, verwenden Sie doch lieber Fork & Exec, obwohl Sie es komplizierter gestalten. Hier ist eine Liste der Unterschiede zwischen ihnen:

Der Befehl "System" 1- erstellt eine Kopie von Shell, um Ihr Programm auszuführen. Bei jedem Aufruf eines Systems erstellen Sie eine Kopie von Shell. Verwenden Sie es daher nicht, wenn Sie viele Programme in Ihrem Prozess ausführen.

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

3- Da das System Shell zum Ausführen des gewünschten Programms aufruft, können Probleme mit der Benutzerberechtigung auftreten. Zum Beispiel kann jemand Ihren Code knacken und etwas anderes ausführen als das Programm, das Sie durch den Systembefehl ausführen möchten.

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

0
ImanKh

Die Antwort von JonSpencer ist in Ordnung, mit der Ausnahme, dass child_status ein int (kein Zeiger auf int) sein muss und als Referenz an die wait-Funktion übergeben werden muss.

Der Code würde also im Wesentlichen derselbe sein, nur die paar Dinge ä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 nicht genug Ruf habe, um Jons Beitrag zu kommentieren, also habe ich ihn redigiert. Einige Leute lehnten die Edition ab und forderten mich auf, die Frage zu beantworten, anstatt sie zu bearbeiten und klar, um 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!

0
jap jap