web-dev-qa-db-fra.com

Différence entre "système" et "exec" sous Linux?

Quelle est la différence entre les commandes de la famille system et exec? Surtout je veux savoir lequel d'entre eux crée un processus enfant au travail?

63
Kamil

system() appelle sh pour gérer votre ligne de commande afin que vous puissiez obtenir une extension générique, etc. exec() et ses amis remplacent l'image de processus actuelle par une nouvelle image de processus.

Avec system(), votre programme continue de s'exécuter et vous retrouvez un statut concernant la commande externe que vous avez appelée. Avec exec(), votre processus est effacé.

En général, je suppose que vous pourriez penser à system() comme une interface de niveau supérieur. Vous pouvez dupliquer vous-même ses fonctionnalités en utilisant les combinaisons fork(), exec() et wait().

Pour répondre à votre dernière question, system() provoque la création d'un processus enfant, contrairement à la famille exec(). Vous devrez utiliser fork() pour cela.

84
Carl Norum

La fonction exec remplace l’image de processus en cours d’exécution en cas de succès, aucun enfant n’est créé (à moins que vous ne le fassiez vous-même auparavant avec fork()). La fonction system () crée un processus enfant et retourne lorsque l'exécution de la commande fournie est terminée ou qu'une erreur survient.

19
BobbyShaftoe

system() exécutera la commande fournie dans un processus enfant qu'il génère. exec() remplacera le processus en cours par l'appel du nouvel exécutable que vous spécifiez. Si vous souhaitez générer un processus enfant à l'aide de exec, vous devez au préalable fork() votre processus.

7
Timo Geusch

Pour créer un processus:

  • fork(2), un appel système directement au noyau

Pour exécuter un programme en remplaçant l'image actuelle:

  • execve(2), un appel système directement au noyau, généralement appelé exec

Pour attendre la fin d'un processus enfant:

  • wait(2), un appel système directement au noyau

Pour exécuter un programme dans un shell dans un processus enfant et attendre sa fin:

  • system(3), une fonction de bibliothèque

Pour obtenir les pages de manuel pour tout ce qui précède:

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

system () invoquera la commande par défaut de votre système Shell, qui exécutera la chaîne de commande transmise en tant qu'argument, pouvant elle-même créer d'autres processus, qui dépendrait de la commande et du système. Dans tous les cas, au moins un processus de commande sera créé.

Avec system (), vous pouvez appeler n'importe quelle commande, alors qu'avec exec (), vous ne pouvez invoquer qu'un fichier exécutable. Les scripts shell et les fichiers de commandes doivent être exécutés par la commande Shell.

Fondamentalement, ils sont complètement différents utilisés à des fins différentes. De plus, exec () remplace le processus d'appel et ne retourne pas. Une comparaison plus utile serait entre system () et spawn (). Alors que le système peut être plus simple à appeler, il renvoie une valeur qui vous indique si la commande Shell a été appelée et ne vous dit rien sur le succès de la commande elle-même. Avec spawn (), vous pouvez obtenir le code de sortie du processus; par convention, non nul est utilisé pour indiquer les conditions d'erreur. Comme exec (), spawn () doit appeler un fichier exécutable et non un script shell ou une commande intégrée.

2
Clifford

int system(const char *cmdstring);

Ex: system("date > file");


En général, system est implémenté en appelant fork, exec et waitpid , il existe trois types de valeurs de retour.

  • Si le fork échoue ou que waitpid renvoie une erreur autre que EINTR, le système renvoie –1 avec errno dans la liste errnopour indiquer l'erreur.
  • Si l'exécution échoue, ce qui implique que le shell ne peut pas être exécuté, la valeur de retour est comme si le shell avait exécuté Exit (127).
  • Sinon, les trois fonctions (fork, exec et waitpid) réussissent et la valeur renvoyée par system est l'état de fin du shell, au format spécifié pour waitpid.

La fonction fork consiste à créer un nouveau processus (l'enfant) qui, ensuite, provoque l'exécution d'un autre programme en appelant l'une des fonctions exec . Lorsqu'un processus appelle l'une des fonctions Exec, ce processus est complètement remplacé par le nouveau programme et celui-ci commence à s'exécuterat dans sa fonction principale. L'identifiant de processus ne change pas dans un exec, car aucun nouveau processus n'est créé. exec remplace simplement le processus en cours (ses segments texte, données, tas et pile) par un tout nouveau programme à partir du disque

Il y a six fonctions différentes exec ,


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

Il convient de garder à l'esprit certaines différences significatives entre exec(2) et system(3). system() renvoie à l'appelant, alors que exec() remplace le code existant par la nouvelle image. Cela a été expliqué ci-dessus.

Cependant, la différence, pas si subtile, se produit lorsque vous souhaitez exécuter une procédure, puis revenir à votre code existant, en recevant le code de retour de la procédure invoquée. system() fournit un code de retour, mais ce dernier ne peut être utilisé que pour détecter une condition d'erreur et ne peut pas être utilisé pour récupérer un code de retour.

Une séquence appropriée d'appels système est la suivante:

#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
        }
     }
  }
}

Il existe d’autres subtilités dans cette séquence qui peuvent être déterminées par une lecture attentive des pages de manuel pertinentes, mais ce code fonctionnera correctement en l’absence de signaux, de processus enfants multiples, etc. De plus, les déclarations en ligne peuvent limiter la variables, mais sont incluses pour permettre à ce code d’être utilisé comme un modèle qui fonctionne (vous pouvez utiliser un style de codage différent :-).

1
Jon Spencer

exec () remplace le processus en cours d'exécution par la mémoire image de la fonction en cours d'exécution. Seuls les fichiers exécutables peuvent être appelés à l'aide de cette procédure.

system () supprime implicitement un nouveau processus pour traiter la demande et renvoie la valeur obtenue via le processus enfant qu'il a initialement créé. Il utilise le shell par défaut du système pour effectuer l'opération.

1
Morpheus

system () appelle le programme souhaité ou la commande intégrée à l'aide d'un shell. Cette méthode est inefficace car un shell est démarré avant le programme. 

Dans le cas de la famille d'appels système exec, une toute nouvelle image est en cours de création, c'est-à-dire qu'elle remplace le processus en cours par un nouveau processus spécifié par le chemin ou le fichier ou par l'argument que vous mentionnez.

Il convient de garder à l'esprit que, lorsque la famille d'appels système exec est utilisée, le programme d'origine ne sera plus exécuté après le démarrage du nouveau.

0
kishanp

System () créera un processus enfant et invoquera un autre sous-shell tandis qu'exec () ne créera pas de processus enfant. Exemple donné effacera la différence.

du code ...

exec ('ls -l')

echo "1 2 3" // Ceci ne sera pas exécuté dans bash (la commande exec utilise le même shell)

du code ...

system (ls -l) echo "1 2 3" // Ceci sera exécuté une fois le processus enfant système terminé car ils sont différents du PID parent. 

0
Poseidon_Geek

En général, "système" est tellement inefficace et vous ne devriez pas l'utiliser sauf si vous avez un petit code. Si vous avez besoin d'exécuter plusieurs programmes dans votre processus, vous feriez mieux d'utiliser fork & exec bien que vous le rendiez plus compliqué . Voici une liste des différences entre eux:

1- La commande "system" crée une copie de Shell pour exécuter votre programme. Chaque fois que vous appelez un système, vous créez une copie de Shell. Donc, ne l'utilisez pas lorsque vous avez beaucoup de programmes à exécuter dans votre processus.

2- Spécifiquement, si vous voulez exécuter des fonctions système telles que "mv", "mkdir", il serait préférable d'utiliser des routines telles que mkdir (), unlink () ou remove () au lieu de les exécuter avec "system (") rm .... ") ou system (" mkdir .... ")".

3- Puisque le système appelle Shell pour exécuter le programme souhaité, il se peut que certains problèmes d’autorisation d’utilisateur soient rencontrés. Par exemple, quelqu'un peut déchiffrer votre code et exécuter autre chose à la place du programme que vous souhaitez exécuter via la commande système.

Pour plus d'informations, vous pouvez lire le chapitre 11 de ce livre: "Programmation de systèmes UNIX" de David Curry.

0
ImanKh

JonSpencer répond, c'est bien, sauf que child_status doit être un int (pas de pointeur sur int) et doit être passé à wait, fonction par référence.

Donc, le code serait essentiellement le même, changeant simplement ces quelques choses:

#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
        }
     }
  }
}

(Faites remarquer que je n'ai pas encore assez de réputation pour commenter le message de Jon. Je l'ai donc modifié. Certaines personnes ont rejeté l'édition, me demandant de répondre à la question plutôt que de la modifier, mais je pense que dans ce cas, c'est beaucoup plus simple et pratique. et clair pour éditer un code existant en corrigeant juste une petite erreur que d’écrire une réponse complète copier/coller/modifier.) En tout cas, merci JonSpencer pour votre réponse, c’était vraiment utile pour moi!

0
jap jap