Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Introduzione ai sistemi operativi

Ciao, Habr! Vorrei presentarvi una serie di articoli tradotti su una libreria che trovo interessante: OSTEP. In questo materiale si analizza in profondità il funzionamento dei sistemi operativi simili a Unix, in particolare la gestione dei processi, vari pianificatori, la memoria e altri componenti simili che costituiscono un moderno sistema operativo. Potete visualizzare l'originale di tutti i materiali qui qui. Si prega di notare che la traduzione non è stata eseguita da un professionista (è abbastanza libera), ma spero di aver mantenuto il significato generale.

Le esercitazioni su questo argomento possono essere trovate qui:

Altre parti:

E potete anche dare un'occhiata al mio canale su Telegram =)

Allerta! c'è un lab a questa lezione! guarda github

Processo API

Esaminiamo un esempio di creazione di un processo in un sistema UNIX. Questo avviene tramite due chiamate di sistema fork() e exec().

Chiamata fork()

Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Esaminiamo il programma che esegue la chiamata fork(). Il risultato della sua esecuzione sarà il seguente.

Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Per prima cosa entriamo nella funzione main() ed effettuiamo la stampa della stringa sullo schermo. La stringa contiene l'identificatore del processo che originariamente è chiamato PID o identificatore di processo. Questo identificatore è utilizzato in UNIX per riferirsi a un processo. Il comando successivo eseguirà la chiamata fork(). In questo momento viene creata una copia quasi esatta del processo. Per il sistema operativo, sembra che siano in esecuzione due copie della stessa programma, che a loro volta eseguiranno la funzione fork(). Il nuovo processo figlio (rispetto al processo genitore che lo ha creato) non verrà eseguito a partire dalla funzione main(). È importante ricordare che il processo figlio non è una copia esatta del processo genitore; in particolare, ha il proprio spazio indirizzi, i propri registri, un proprio puntatore alle istruzioni eseguibili e così via. Pertanto, il valore restituito alla funzione fork() sarà diverso. In particolare, il processo genitore riceverà come ritorno il PID del processo bambino, mentre il bambino riceverà un valore pari a 0. Con questi codici di ritorno, è possibile distinguere i processi e far eseguire a ciascuno di essi il proprio compito. Tuttavia, l'esecuzione di questo programma non è rigorosamente definita. Dopo la divisione in due processi, il sistema operativo inizia a monitorarli e pianificarne il funzionamento. Nel caso di esecuzione su un processore a singolo core, il lavoro continuerà con uno dei processi, in questo caso il genitore, e poi il controllo passerà al processo figlio. Al successivo avvio, la situazione potrebbe essere diversa.

Chiamata wait()

Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Consideriamo il seguente programma. In questo programma, grazie alla presenza della chiamata wait() il processo genitore attenderà sempre il completamento del processo figlio. In questo caso otterremo un'uscita del testo sullo schermo strettamente definita.

Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Chiamata exec()

Operating Systems: Three Easy Pieces. Part 3: Process API (traduzione)

Consideriamo la chiamata exec(). Questa chiamata di sistema è utile quando vogliamo eseguire un programma completamente diverso. Qui chiameremo execvp() per avviare il programma wc, che è un programma di conteggio parole. Cosa succede quando viene chiamato exec()? A questa chiamata vengono passati come argomenti il nome del file eseguibile e alcuni parametri. Dopodiché avviene il caricamento del codice e dei dati statici da questo file eseguibile e si sovrascrive il proprio segmento di codice. Altre aree di memoria, come lo stack e l'heap, vengono reinizializzate. Successivamente, il sistema operativo esegue semplicemente il programma, passando un insieme di argomenti. In questo modo, non abbiamo creato un nuovo processo, ma abbiamo semplicemente trasformato il programma in esecuzione in un altro programma in esecuzione. Dopo l'esecuzione della chiamata exec(), nel processo figlio sembra che il programma originale non sia stato avviato affatto.

Questo complicazione dell'avvio è del tutto normale per la shell Unix, e consente a questa shell di eseguire codice dopo la chiamata fork(), ma prima della chiamata exec(). Un esempio di tale codice potrebbe essere l'adattamento dell'ambiente della shell alle esigenze del programma che deve essere avviato, prima del suo avvio effettivo.

Shell — solo un programma utente. Mostra una riga di comando e aspetta che ci scriviate qualcosa. Nella maggior parte dei casi, se scrivete il nome del programma, il shell ne troverà il percorso, chiamerà il metodo fork(), e poi per creare un nuovo processo chiamerà uno dei tipi di exec() e attenderà la sua esecuzione tramite una chiamata a wait(). Quando il processo figlio termina, il shell ritornerà dalla chiamata a wait() e mostrerà di nuovo la riga di comando, pronto ad accettare il prossimo comando.

La separazione di fork() & exec() consente al shell di fare diverse cose, per esempio:
wc file > new_file.

In questo esempio, l'output del programma wc è reindirizzato in un file. Il modo in cui il shell raggiunge questo è piuttosto semplice: quando crea il processo figlio prima di chiamare exec(), il shell chiude il flusso di output standard e apre il file new_file, in questo modo, tutto l'output del programma successivamente avviato wc sarà reindirizzato nel file invece che sullo schermo.

Unix pipe realizzati in modo simile, con la differenza che utilizzano la chiamata pipe(). In questo caso, il flusso di uscita del processo sarà collegato alla coda pipe, situata nel kernel, alla quale sarà anche collegato il flusso di ingresso di un altro processo.

Fonte: habr.com

Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster