Comment un shell exécute-t-il un programme?

11

Si je compile un programme à l'aide de gcc et essaie de l'exécuter à partir du shell bash, quelle est la séquence exacte des étapes suivies par bash pour l'exécuter?

Je sais fork(), execve(), loader, dynamic linker(et autres) sont impliqués, mais quelqu'un peut - il donner une suite exacte des étapes et une référence de lecture appropriée?

Éditer:

D'après les réponses, il semble que la question pourrait impliquer de nombreuses possibilités. Je veux me limiter à un cas simple:

(test.c imprime juste bonjour le monde)

$ gcc test.c -o test
$ ./test

Quelles seront les étapes dans le cas ci-dessus ( ./test), concernant spécifiquement le démarrage du programme bash dans un processus enfant, le chargement, la liaison, etc.?

Jake
la source
4
Je vous invite à lire lwn.net/Articles/630727
cuonglm
3
Pourquoi ne pas essayer `strace bash -c 'test'?
Sergiy Kolodyazhnyy
2
Il semble qu'un manuel de systèmes d'exploitation décent serait une bonne ressource pour l'OP. Essayer de comprendre le fonctionnement des systèmes d'exploitation en posant des questions individuelles comme celle-ci ne sera probablement pas un processus productif.
Barmar
Il serait utile de voir un exemple minimal d'un shell: brennan.io/2015/01/16/write-a-shell-in-c
jinawee

Réponses:

5

Eh bien, la séquence exacte peut varier, car il peut y avoir un alias ou une fonction shell qui soit d'abord développé / interprété avant l'exécution du programme réel, puis des différences pour un nom de fichier qualifié ( /usr/libexec/foo) par rapport à quelque chose qui sera recherché dans tous les répertoires de la PATHvariable d'environnement (juste foo). De plus, les détails de l'exécution peut compliquer les choses, comme foo | bar | zotexige plus de travail pour la coquille ( un certain nombre de fork(2), dup(2)et, bien sûr, pipe(2)entre autres appels système), alors que quelque chose comme exec foobeaucoup moins de travail que la coque elle - même remplace simplement avec le nouveau programme (c'est-à-dire qu'il ne fonctionne pas fork). Les groupes de processus sont également importants (en particulier le groupe de processus de premier plan, dont tous les PIDSIGINTlorsque quelqu'un commence à écraser Ctrl+ C, les sessions et si le travail va être exécuté en arrière-plan, surveillé ( foo &) ou en arrière-plan, ignoré ( foo & disown). Les détails de la redirection d'E / S changeront également les choses, par exemple, si l'entrée standard est fermée par le shell ( foo <&-), ou si un fichier est ouvert en tant que stdin ( foo < blah).

straceou similaire fournira des informations sur les appels système spécifiques effectués au cours de ce processus, et il devrait y avoir des pages de manuel pour chacun de ces appels. Une lecture appropriée au niveau du système serait un certain nombre de chapitres de la "Programmation avancée dans l'environnement UNIX" de Stevens, tandis qu'un livre shell (par exemple, "De Bash à Z Shell") couvrira le côté shell des choses plus en détail.

branler
la source
J'ai édité la question pour me limiter à un cas simple
Jake
1

En supposant qu'un exemple de shell de manuel (pour la clarté du code) soit déjà en cours d'exécution (de sorte que l'éditeur de liens dynamique soit terminé), les commandes que vous mentionnez nécessiteront que le shell effectue les appels système suivants:

  • lire: obtient la commande suivante dans ce cas gcc
  • fork: deux processus sont nécessaires, nous supposons que le parent a pid 500 et l'enfant à titre d'illustration.
  • le parent appellera wait (501), tandis que l'enfant appellera exec. À ce stade, le shell ne fonctionne plus sur le pid 501. gcc effectue de nombreux appels système, y compris au minimum pour ouvrir, fermer, lire, écrire, chmod, fork, exec, attendre et quitter.
  • lorsque gcc appelle exit, attendre reviendra, write sera appelé pour afficher l'invite et le processus se répétera.

Des commandes plus compliquées ajoutent bien sûr plus de complication à cette séquence de base. Deux exemples plus simples de complications de base sont la redirection io de base où une séquence de dup ouverte, fermée et insérée est insérée entre le fork et les processus exec et d'arrière-plan où l'attente est ignorée (et une autre attente est ajoutée à un gestionnaire sigchld).

hildred
la source
Petit ajout: la question porte sur le chargement et la liaison dynamique. Tout le code lié statiquement, c'est-à-dire réellement inclus dans le fichier programme, est fait par le noyau avant le démarrage du programme. Les bibliothèques chargées dynamiquement, c'est-à-dire les fichiers séparés, sont gérées par le programme lui-même avant de démarrer main (). Le code pour cela est automatiquement ajouté par gcc.
Stig Hemmer