web-dev-qa-db-fra.com

Comment execvp exécute une commande?

Je sais que execvp peut être utilisé pour exécuter des commandes simples comme suit:

char* arg[] = {"ls", "-l", NULL};
execvp(arg[0],arg);

Je veux savoir ce qui se passe ici lorsque j'exécute execvp. Dans la page de manuel, il est indiqué que execvp remplace l'image de l'image de processus par la nouvelle. Cependant, ici, je lance une commande et non un exécutable.

Pour être précis, disons qu'il existe une commande qui nécessite spécifiquement une entrée, par exemple chat. Si j'ai un fichier texte text.txt qui contient le nom de fichier attendu pour cat et que je redirige stdin vers le flux de fichiers du fichier, la sortie de execle("cat","cat",NULL) ou execvp("cat", arg) (évidemment où arg stocke "cat" et NULL) donnent la sortie dans la console comme le ferait cat /filename? Mon intuition est que je dois lire le fichier et peut-être l'analyser pour stocker les arguments dans l'argument. Cependant, je veux m'assurer.

Merci d'avance!

21
as3rdaccount

Voici ce qui se passe dans un appel execvp:

  1. Votre implémentation libc recherche dans PATH, le cas échéant, le fichier à exécuter. La plupart, sinon la totalité, des commandes des systèmes de type UNIX sont des exécutables. Que se passera-t-il si ce n'est pas le cas? Essayez-le. Jetez un oeil à comment la glibc le fait .
  2. En règle générale, si l'exécutable est trouvé, un appel à execve sera effectué. Des parties de execve peuvent être implémentées dans libc ou ce peut être un appel système (comme sous Linux).
  3. Linux prépare un programme en lui allouant de la mémoire, en l'ouvrant, en le planifiant pour l'exécution, initialise les structures de mémoire, configure ses arguments et son environnement à partir des arguments fournis à l'appel execvp, trouve un gestionnaire approprié pour charger le binaire et définit la tâche en cours (l'appelant execvp) comme ne s'exécutant pas. Vous pouvez trouver son implémentation ici .

Toutes les étapes ci-dessus sont conformes aux exigences définies par POSIX qui sont décrites dans les pages de manuel pertinentes.

19
Michael Foukarakis

Concernant vos questions:

Dans la page de manuel, il est indiqué que execvp remplace l'image de l'image de processus par la nouvelle. Cependant, ici, je lance une commande et non un exécutable.

Il y a très longtemps, Shell était très limité et presque toutes les commandes UNIX étaient des exécutables autonomes. Maintenant, principalement à des fins de vitesse, un sous-ensemble de commandes UNIX est implémenté à l'intérieur de Shell lui-même, ces commandes sont appelées builtins. Vous pouvez vérifier quelle que soit la commande implémentée dans votre shell comme intégrée ou non via la commande type:

λ ~/ type echo
echo is a Shell builtin

(La liste complète des fonctions intégrées avec des descriptions peut être trouvée dans les pages man de votre shell, par exemple man bash-builtins ou man builtin.)

Mais la plupart des commandes ont toujours leur équivalent exécutable:

λ ~/ whereis echo
/bin/echo

Donc, dans votre cas spécifique lorsque vous exécutez:

char* arg[] = {"ls", "-l", NULL};
execvp(arg[0],arg);

Vous remplacez actuellement l'espace d'adressage du processus en cours par l'espace d'adressage (le plus probable) /bin/ls.


Mon intuition est que je dois lire le fichier et peut-être l'analyser pour stocker les arguments dans l'argument.

En effet, vous l'avez. Mais vous pouvez également utiliser certaines fonctions dans le noyau pour cela aka "Shebang":
Au lieu de mettre le nom du fichier dans un fichier séparé, ajoutez ce qu'on appelle Shebang comme première ligne du fichier que vous souhaitez cat:

#!/bin/cat

Et ajouter chmod +x à elle. Ensuite, vous pouvez l'exécuter en tant qu'exécutable (via l'une des fonctions exec ou Shell):

λ ~/tmp/ printf '#!/bin/cat\nTEST\n' > cat_me
λ ~/tmp/ chmod +x cat_me
λ ~/tmp/ ./cat_me 
#!/bin/cat
TEST

Il y a un inconvénient à imprimer Shebang lui-même avec un fichier mais c'est toujours amusant de le faire dans le noyau =)

BTW. Problème que vous avez décrit s'il est si courant qu'il existe un exécutable spécial appelé xargs qui (dans une explication très simplifiée) exécute le programme donné sur la liste des arguments passés via stdin. Pour plus d'informations, consultez man xargs.


Pour une mémorisation facile de la famille exec- j'utilise souvent le tableau suivant:

           Figure 8.14. Differences among the six exec functions
+----------+----------+----------+----------+--------+---------+--------+
| Function | pathname | filename | agr list | argv[] | environ | envp[] |
+----------+----------+----------+----------+--------+---------+--------+
|  execl   |    *     |          |     *    |        |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execlp  |          |    *     |     *    |        |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execle  |    *     |          |     *    |        |         |   *    |
+----------+----------+----------+----------+--------+---------+--------+
|  execv   |    *     |          |          |    *   |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execvp  |          |    *     |          |    *   |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execve  |    *     |          |          |    *   |         |   *    |
+----------+----------+----------+----------+--------+---------+--------+
|  letter  |          |    p     |     l    |    v   |         |   e    |
+----------+----------+----------+----------+--------+---------+--------+

Donc dans votre cas execvp prend le nom de fichier, argv ( v ) et environ ( e ). Ensuite, il essaie de "deviner" le nom de chemin (alias chemin complet) en ajoutant filename (dans votre cas cat) à chaque composant de chemin dans PATH jusqu'à ce qu'il trouve le chemin avec l'exécutable filename.

Beaucoup plus d'informations sur ce qui se passe sous le capot de exec (y compris les éléments d'héritage) peuvent être trouvées dans Programmation avancée dans l'environnement UNIX (2e édition) par W. Richard Stevens et Stephen A. Rago aka APUE2.
Si vous êtes intéressé par les composants internes UNIX, vous devriez probablement le lire.

12
SaveTheRbtz

"ls" n'est pas seulement une commande, c'est en fait un programme (la plupart des commandes le sont). Lorsque vous exécutez execvp comme ça, il nuke tout votre programme, sa mémoire, sa pile, son tas, etc ... conceptuellement "effacez-le" et donnez-le à "ls" afin qu'il puisse l'utiliser pour sa propre pile, tas, etc.

En bref, execvp détruira votre programme et le remplacera par un autre programme, dans ce cas "ls".

2
Verdagon

Mon intuition est que je dois lire le fichier et peut-être l'analyser pour stocker les arguments dans l'argument. Cependant, je veux m'assurer.

Votre intuition est largement correcte. L'utilitaire cat que vous utilisez comme exemple a deux chemins de code distincts:

  • S'il y a des noms de fichiers spécifiés comme arguments, il s'ouvrira et les lira chacun à son tour.
  • Si aucun nom de fichier n'est spécifié, il sera lu à partir de l'entrée standard.

Ce comportement est spécifiquement implémenté dans l'utilitaire cat - il n'est implémenté à aucun niveau inférieur. En particulier, il ne fait définitivement pas partie de l'appel système exec. Les appels système exec ne "regardent" pas du tout les arguments; ils les transmettent directement au nouveau processus dans argv, et ce processus peut les gérer comme bon lui semble.

1
duskwuff