Donc, je pensais que j'avais une bonne compréhension de cela, mais j'ai juste fait un test (en réponse à une conversation où je n'étais pas d'accord avec quelqu'un) et j'ai trouvé que ma compréhension était imparfaite ...
De manière aussi détaillée que possible, que se passe-t-il exactement lorsque j'exécute un fichier dans mon shell? Ce que je veux dire, c’est que si je tape: ./somefile some arguments
dans mon shell et appuie sur Entrée (et somefile
existe dans cwd, et j’ai lu + exécuté les autorisations sur somefile
), que se passe-t-il sous le capot?
Je pensais que la réponse était:
- Le shell fait un appel système en
exec
passant le chemin àsomefile
- Le noyau examine
somefile
et examine le nombre magique du fichier pour déterminer s'il s'agit d'un format que le processeur peut gérer. - Si le nombre magique indique que le fichier est dans un format que le processeur peut exécuter, alors
- un nouveau processus est créé (avec une entrée dans la table de processus)
somefile
est lu / mappé à la mémoire. Une pile est créée et l'exécution saute au point d'entrée du code desomefile
, avecARGV
initialisé à un tableau de paramètres (achar**
,["some","arguments"]
)
- Si le numéro de magie est un tralala puis
exec()
génère un nouveau procédé comme ci - dessus, mais l'exécutable utilisé est l'interpréteur référencé par le tralala (par exemple ,/bin/bash
ou/bin/perl
) etsomefile
est passé àSTDIN
- Si le fichier n'a pas de numéro magique valide, une erreur du type "fichier invalide (mauvais numéro magique): erreur de format Exec" se produit
Cependant, quelqu'un m'a dit que si le fichier est en texte brut, le shell tente d'exécuter les commandes (comme si j'avais tapé bash somefile
). Je n'y croyais pas, mais je l'ai juste essayé et c'était correct. J'ai donc clairement des idées fausses sur ce qui se passe réellement ici et j'aimerais comprendre les mécanismes.
Que se passe-t-il exactement lorsque j'exécute un fichier dans mon shell? (autant de détails est raisonnable ...)
source somefile
est très différent d’un nouveau processus déclenché par./somefile
, cependant../somefile
cela entraînerait l’exécution des commandes par bashsomefile
si le fichier n’avait pas de numéro magique. Je pensais que cela afficherait simplement une erreur, et à la place, cela semble être efficacesource somefile
somefile
s'agit d'un fichier texte, un nouveau shell est créé si j'essaie de l'exécuter. Un fichierecho $$
se comporte différemment si je l'exécute par rapport à son source.Réponses:
La réponse définitive à "comment les programmes sont exécutés" sur Linux est la paire d'articles sur LWN.net intitulée, assez étonnamment, Comment les programmes sont exécutés et Comment les programmes sont exécutés: les fichiers binaires ELF . Le premier article traite brièvement des scripts. (À proprement parler, la réponse définitive se trouve dans le code source, mais ces articles sont plus faciles à lire et fournissent des liens vers le code source.)
Un peu d’expérimentation montre que vous avez très bien compris et que l’exécution d’un fichier contenant une simple liste de commandes, sans shebang, doit être gérée par le shell. La page de manuel execve (2) contient le code source d’un programme de test, execve; nous l'utiliserons pour voir ce qui se passe sans shell. Commencez par écrire un test
testscr1
, contenantet un autre,
testscr2
ne contenant queRendez-les exécutables et vérifiez qu’ils s’exécutent tous les deux à partir d’un shell:
Maintenant, essayez à nouveau, en utilisant
execve
(en supposant que vous l’aviez construit dans le répertoire courant):testscr1
fonctionne toujours, maistestscr2
produitCela montre que le shell gère
testscr2
différemment. Il ne traite pas le script lui-même, il l'utilise toujours/bin/sh
pour le faire. ceci peut être vérifié entestscr2
passant àless
:Sur mon système, je reçois
Comme vous pouvez le constater, il y a le shell que j'utilisais
zsh
, qui a démarréless
, et un deuxième shell, en clairsh
(dash
sur mon système), permettant d'exécuter le script, qui s'est exécutépstree
. Danszsh
c'est gérée parzexecve
dansSrc/exec.c
: l'utilisation shellexecve(2)
pour tenter d'exécuter la commande, et si cela échoue, il lit le fichier pour voir si elle a un tralala, la traiter en conséquence (que le noyau aura également fait), et si cela échoue, il essaie d'exécuter le fichier avecsh
, tant qu'il n'a pas lu d'octet zéro dans le fichier:bash
a le même comportement, implémentéexecute_cmd.c
avec un commentaire utile (comme l'a souligné taliezin ):POSIX définit un ensemble de fonctions, connu sous les
exec(3)
fonctions , qui enveloppeexecve(2)
et fournir cette fonctionnalité aussi; voir la réponse de muru pour plus de détails. Sous Linux au moins, ces fonctions sont implémentées par la bibliothèque C, pas par le noyau.la source
Cela dépend en partie de la
exec
fonction familiale utilisée.execve
, comme Stephen Kitt l’ a montré en détail, n’exécute que les fichiers au format binaire approprié ou les scripts commençant par un shebang approprié.Cependant ,
execlp
etexecvp
allez un peu plus loin: si le shebang n’est pas correct, le fichier est exécuté/bin/sh
sous Linux. Deman 3 exec
:Ceci est quelque peu soutenu par POSIX (c'est moi qui souligne):
Ceci ne spécifie pas comment l’interpréteur de commande est obtenu, donc, mais ne spécifie pas qu’une erreur doit être donnée. Je suppose donc que les développeurs Linux ont autorisé l’utilisation de tels fichiers
/bin/sh
(ou c’était déjà une pratique courante et ils ont juste emboîté le pas).FWIW, la page de
exec(3)
manuel FreeBSD pour mentionne également un comportement similaire:AFAICT, cependant, aucun shell commun n’utilise
execlp
ouexecvp
directement, vraisemblablement pour un contrôle plus fin de l’environnement. Ils mettent tous en œuvre la même logique en utilisantexecve
.la source
execl
,execlp
,execle
,execv
,execvp
etexecvpe
sont tous les frontaux à laexecve
syscall; les premiers sont fournis par la bibliothèque C, le noyau n’en connaît queexecve
(et deexecveat
nos jours).Cela pourrait être un ajout à la réponse de Stephen Kitt, en tant que commentaire de la
bash
source dans le fichierexecute_cmd.c
:la source
Il est exécuté en tant que script shell, il n'est pas généré (par exemple, les variables définies dans le fichier exécuté ne concernent pas l'extérieur). Probablement vestigiale du passé brumeux, quand il y avait un shell et un format exécutable. Ce n'est pas un exécutable, ce doit être un script shell.
la source
exec()
ou le shell? Je veux beaucoup plus d'internes