In che modo una shell esegue un programma?


11

Se compilo un programma usando gcc e provo ad eseguirlo dalla shell bash, qual è l'esatta sequenza di passi seguita da bash per eseguirlo?

So fork(), execve(), loader, dynamic linker(e altre cose) sono coinvolti, ma è possibile che qualcuno faccia un esatta sequenza di passi e un po 'di riferimento adeguati lettura?

Modificare:

Dalle risposte, sembra che la domanda potrebbe implicare molte possibilità. Voglio limitarmi a un semplice caso:

(test.c stampa solo ciao mondo)

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

Quali saranno i passaggi nel caso precedente ( ./test), in particolare relativi al programma di avvio bash in alcuni processi figlio, caricamento, collegamento ecc.?


4
Ti invito a leggere lwn.net/Articles/630727
cuonglm

3
Perché non provare `strace bash -c 'test'?
Sergiy Kolodyazhnyy,

2
Sembra che un buon libro di testo sui sistemi operativi sarebbe una buona risorsa per il PO. Cercare di imparare come funzionano i sistemi operativi ponendo domande individuali come questa non è probabilmente un processo produttivo.
Barmar,

Sarebbe utile vedere un esempio minimo di shell: brennan.io/2015/01/16/write-a-shell-in-c
jinawee

Risposte:


5

Bene, la sequenza esatta può variare, poiché potrebbe esserci un alias di shell o una funzione che viene prima espansa / interpretata prima che venga eseguito il programma vero e proprio, e quindi differenze per un nome file qualificato ( /usr/libexec/foo) rispetto a qualcosa che verrà cercato in tutte le directory della PATHvariabile d'ambiente (solo foo). Inoltre, i dettagli dell'esecuzione possono complicare le cose, poiché foo | bar | zotrichiede più lavoro per la shell (un certo numero di fork(2), dup(2)e, ovviamente pipe(2), tra le altre chiamate di sistema), mentre qualcosa di simile exec fooè molto meno lavoro poiché la shell si sostituisce semplicemente con il nuovo programma (cioè, non lo fa fork). Importanti sono anche i gruppi di processi (in particolare il gruppo di processi in primo piano, tutti i PID di cui mangianoSIGINTquando qualcuno inizia a eseguire il mashing su Ctrl+ C, sessioni e se il lavoro verrà eseguito in background, monitorato ( foo &) o in background, ignorato ( foo & disown). I dettagli di reindirizzamento I / O cambieranno anche cose, ad esempio, se l'input standard è chiuso dalla shell ( foo <&-) o se un file viene aperto come stdin ( foo < blah).

straceo simili saranno informativi sulle chiamate di sistema specifiche fatte lungo questo processo, e ci dovrebbero essere pagine man per ciascuna di quelle chiamate. La lettura adeguata a livello di sistema sarebbe un numero qualsiasi di capitoli di "Advanced Programming in the UNIX Environment" di Stevens mentre un libro di shell (ad esempio, "Da Bash a Z Shell") tratterà il lato shell delle cose in modo più dettagliato.


Ho modificato la domanda per restringere il campo a un semplice caso
Jake,

1

Supponendo una shell di esempio da manuale (per chiarezza del codice) che è già in esecuzione (quindi viene eseguito il linker dinamico), i comandi menzionati richiederanno alla shell di effettuare le seguenti chiamate di sistema:

  • leggi: ottiene il comando successivo in questo caso gcc
  • fork: sono necessari due processi, supponiamo che il genitore abbia pid 500 e il figlio come illustrazione.
  • il genitore chiamerà wait (501), nel frattempo il bambino chiamerà exec. A questo punto la shell non è più in esecuzione su pid 501. gcc effettua molte chiamate di sistema tra cui almeno aprire, chiudere, leggere, scrivere, chmod, fork, exec, wait e exit.
  • quando gcc chiama exit, wait ritorna, viene chiamato write per visualizzare il prompt e il processo si ripeterà.

Comandi più complicati ovviamente aggiungono ulteriori complicazioni a questa sequenza di base. Due esempi più semplici di complicazioni di base sono il reindirizzamento di base in cui viene inserita una sequenza duplex aperta, chiusa, tra la forcella e i processi exec e background in cui viene ignorata l'attesa (e un'altra attesa viene aggiunta a un gestore sigchld).


Piccola aggiunta: la domanda pone sul caricamento e sul collegamento dinamico. Tutto il codice che è staticamente collegato, cioè effettivamente incluso nel file di programma, viene eseguito dal kernel prima dell'avvio del programma. Le librerie caricate dinamicamente, ovvero file separati, sono gestite dal programma stesso prima di avviare main (). Il codice per questo viene aggiunto automaticamente da gcc.
Stig Hemmer,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.