Pourquoi devons-nous passer le nom de fichier deux fois dans les fonctions exec?

11

J'ai lu la programmation avancée dans l'environnement UNIX par Stevens, 8 e chapitre. J'ai lu et compris les six fonctions exécutives.

Une chose que je remarque est, dans toutes les fonctions exécutables:

  • le premier argument est le nom de fichier / nom de chemin (dépend de la fonction exec).
  • le deuxième argument est argv [0] dans lequel nous entrons main(), qui est le nom du fichier lui-même.

Nous devons donc ici passer deux fois le nom du fichier dans la fonction.

Y a-t-il une raison à cela (comme si nous ne pouvons pas obtenir le nom de fichier à partir du nom de chemin du premier argument)?

munjal007
la source

Réponses:

14

Nous devons donc ici passer deux fois le nom du fichier dans la fonction.

Ce n'est pas tout à fait la même chose que vous remarquez en observant que l' un d'eux est utilisé comme argv[0]valeur. Cela ne doit pas nécessairement être le même que le nom de base de l'exécutable; beaucoup / la plupart des choses l'ignorent et vous pouvez y mettre tout ce que vous voulez.

Le premier est le chemin réel vers l'exécutable, pour lequel il existe une nécessité évidente. Le second est passé au processus ostensiblement comme le nom utilisé pour l'invoquer, mais, par exemple:

execl("/bin/ls", "banana", "-l", NULL);

Fonctionnera bien, en supposant que /bin/lsc'est le bon chemin.

Cependant, certaines applications utilisent argv[0]. Habituellement, ceux-ci contiennent un ou plusieurs liens symboliques $PATH; ceci est courant avec les utilitaires de compression (ils utilisent parfois des wrappers shell à la place). Si vous avez xzinstallé, stat $(which xzcat)montre que c'est un lien vers xz, et man xzcatest le même que celui man xzqui explique "xzcat est équivalent à xz --decompress --stdout". La façon dont xz peut dire comment il a été invoqué est en vérifiant argv[0], ce qui rend ces équivalents:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);
boucle d'or
la source
4
Ah, donc cela expliquerait comment busyboxpeut être ce que vous voulez qu'il soit selon la façon dont vous l'appelez bien?
terdon
3
@terdon, c'est exactement la façon dont le binaire unique pour busybox satisfait tant de commandes différentes.
mah
7
Ce qui signifierait que si /bin/lsétait busybox, il ne saurait pas s'exécuter banana!
Riking
6

Vous n'avez pas à transmettre le nom de fichier deux fois.

Le premier est le fichier qui est réellement exécuté.

Le deuxième argument est ce que devrait être le argv[0]processus, c'est-à-dire ce que le processus devrait voir comme son nom. Par exemple, si vous exécutez à lspartir du shell, le premier argument est /bin/ls, le second est juste ls.

Vous pouvez exécuter un certain fichier et l'appeler autrement via le deuxième argument; le programme peut vérifier son nom et se comporter différemment selon le nom. Cela peut également être fait via des liens durs (ou des liens symboliques), mais cela donne plus de flexibilité.

gémir
la source
En fait, les liens sont la même méthode, car elle définit argv[0]le nom du lien.
goldilocks
Dans le dernier paragraphe, "Vous pouvez exécuter un certain fichier et l'appeler autrement via le deuxième argument; le programme peut vérifier son nom et se comporter" différemment "selon le nom". pouvez-vous s'il vous plaît élaborer ou me donner quelques lectures, je suis nouveau dans cet environnement.
munjal007
La dernière partie de la réponse de goldilocks l'explique.
wurtel
1

Le point à argv[0]retenir est qu'il peut être réglé sur n'importe quoi (y compris NULL). Par convention , argv[0]sera défini sur le chemin à partir duquel l'exécutable a été démarré (par le processus shell quand il le fait execve()).

Si ./fooet dir/barsont deux liens différents (physiques ou symboliques) vers le même exécutable, le démarrage du programme à partir du shell en utilisant les deux chemins sera défini argv[0]sur ./fooet dir/bar, respectivement.

Le fait que ce argv[0]soit possible NULLest souvent ignoré. Le code suivant peut se bloquer pour un NULL argv[0]par exemple (bien que la glibc imprime quelque chose comme <null> à la place pour argv[0]):

if (argc != 3) {
    fprintf(stderr, "%s: expected 2 arguments\n", argv[0]);
    exit(EXIT_FAILURE);
}

Une alternative sur Linux est d'utiliser /proc/self/exepour de tels cas.

Ulfalizer
la source
comment pouvez-vous définir argv [0] à la fois ./foo et dir / bar
munjal007
@ munjal007 Je suis désolé si je n'étais pas clair. Je voulais dire exécuter le programme deux fois: une fois comme ./fooet une fois comme dir/bar. argv[0]sera différent pour ces deux cas (dans chaque cas, ce sera le même que le chemin que vous avez utilisé).
Ulfalizer
@ munjal007 Cela suppose bien sûr que vous l'exécutiez à partir du shell. Le fait est que vous pouvez régler argv[0]n'importe quoi lorsque vous exec*()programmez vous-même. C'est une convention du shell de définir argv[0]le chemin qui a été utilisé pour démarrer le programme (et il est sage de faire la même chose lorsque vous exec*()programmez, car de nombreux programmes inspectent argv[0]et s'attendent à ce qu'il contienne le chemin).
Ulfalizer