GNU/Linux >> Linux Esercitazione >  >> Linux

Cosa succede esattamente quando eseguo un file nella shell?

Quindi, ho pensato di avere una buona comprensione di questo, ma ho appena eseguito un test (in risposta a una conversazione in cui non ero d'accordo con qualcuno) e ho scoperto che la mia comprensione è imperfetta...

Nel modo più dettagliato possibile cosa succede esattamente quando eseguo un file nella mia shell? Quello che voglio dire è, se digito:./somefile some arguments nella mia shell e premi Invio (e somefile esiste nel cwd e ho i permessi di lettura+esecuzione su somefile ) allora cosa succede sotto il cofano?

pensavo la risposta è stata:

  1. La shell effettua una syscall a exec , passando il percorso a somefile
  2. Il kernel esamina somefile ed esamina il numero magico del file per determinare se si tratta di un formato che il processore può gestire
  3. Se il numero magico indica che il file è in un formato che il processore può eseguire, allora
    1. viene creato un nuovo processo (con una voce nella tabella dei processi)
    2. somefile viene letto/mappato in memoria. Viene creato uno stack e l'esecuzione salta al punto di ingresso del codice di somefile , con ARGV inizializzato su un array di parametri (un char** , ["some","arguments"] )
  4. Se il numero magico è uno shebang allora exec() genera un nuovo processo come sopra, ma l'eseguibile utilizzato è l'interprete a cui fa riferimento lo shebang (ad es. /bin/bash o /bin/perl ) e somefile viene passato a STDIN
  5. Se il file non ha un numero magico valido, viene visualizzato un errore come "file non valido (numero magico errato):errore di formato Exec"

Tuttavia qualcuno mi ha detto che se il file è di testo normale, la shell prova a eseguire i comandi (come se avessi digitato bash somefile ). Non ci credevo, ma l'ho appena provato ed era corretto. Quindi ho chiaramente alcune idee sbagliate su ciò che effettivamente accade qui e vorrei capire i meccanismi.

Cosa succede esattamente quando eseguo un file nella mia shell? (in tutti i dettagli è ragionevole...)

Risposta accettata:

La risposta definitiva a "come vengono eseguiti i programmi" su Linux è la coppia di articoli su LWN.net intitolati, sorprendentemente, Come vengono eseguiti i programmi e Come vengono eseguiti i programmi:binari ELF. Il primo articolo affronta brevemente gli script. (A rigor di termini la risposta definitiva è nel codice sorgente, ma questi articoli sono più facili da leggere e forniscono collegamenti al codice sorgente.)

Una piccola sperimentazione mostra che hai praticamente capito bene e che l'esecuzione di un file contenente un semplice elenco di comandi, senza shebang, deve essere gestita dalla shell. La manpage execve(2) contiene il codice sorgente per un programma di test, execve; lo useremo per vedere cosa succede senza una shell. Per prima cosa, scrivi uno script di test, testscr1 , contenente

#!/bin/sh

pstree

e un altro, testscr2 , contenente solo

pstree

Rendili entrambi eseguibili e verifica che entrambi vengano eseguiti da una shell:

chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less

Ora riprova, usando execve (supponendo che tu l'abbia compilato nella directory corrente):

./execve ./testscr1
./execve ./testscr2

testscr1 funziona ancora, ma testscr2 produce

execve: Exec format error

Questo mostra che la shell gestisce testscr2 diversamente. Tuttavia, non elabora lo script stesso, utilizza ancora /bin/sh fare quello; questo può essere verificato collegando testscr2 a less :

./testscr2 | less -ppstree

Sul mio sistema, ricevo

    |-gnome-terminal--+-4*[zsh]
    |                 |-zsh-+-less
    |                 |     `-sh---pstree

Come puoi vedere, c'è la shell che stavo usando, zsh , che è iniziato con less e una seconda shell, semplice sh (dash sul mio sistema), per eseguire lo script, che ha eseguito pstree . In zsh questo è gestito da zexecve in Src/exec.c :la shell usa execve(2) per provare a eseguire il comando e, se fallisce, legge il file per vedere se ha uno shebang, elaborandolo di conseguenza (cosa che avrà fatto anche il kernel), e se fallisce prova a eseguire il file con sh , purché non abbia letto zero byte dal file:

        for (t0 = 0; t0 != ct; t0++)
            if (!execvebuf[t0])
                break;
        if (t0 == ct) {
            argv[-1] = "sh";
            winch_unblock();
            execve("/bin/sh", argv - 1, newenvp);
        }

bash ha lo stesso comportamento, implementato in execute_cmd.c con un utile commento (come sottolineato da taliezin):

Esegui un semplice comando che si spera sia definito in un file del disco
da qualche parte.

  1. fork ()
  2. collegare i tubi
  3. Cerca il comando
  4. esegui reindirizzamenti
  5. execve ()
  6. Se il execve fallito, controlla se il file ha la modalità eseguibile impostata.
    Se è così, e non è una directory, esegui il suo contenuto come
    uno script di shell.

POSIX definisce un insieme di funzioni, noto come exec(3) funzioni, che avvolgono execve(2) e fornire anche questa funzionalità; vedere la risposta di muru per i dettagli. Su Linux almeno queste funzioni sono implementate dalla libreria C, non dal kernel.

Correlati:lo scopo della parola chiave "do" in Bash for loop?
Linux
  1. Rileva il sistema Init usando la shell?

  2. Linux:cosa succede quando esegui la sincronizzazione senza un percorso di destinazione??

  3. Cosa succede quando viene superato il limite di larghezza di banda?

  4. Cosa fa il permesso di esecuzione?

  5. Cosa determina esattamente se un lavoro in background viene interrotto quando la shell viene chiusa o terminata?

Che cos'è la shell in Linux?

Cosa succede quando eseguo il comando Cat /proc/cpuinfo?

Come eseguire un file .sh all'inizio della sessione?

Cosa succede in background quando esegui il comando "useradd" in Linux

Cos'è un file .sh?

Qual è la shell predefinita di Busybox?