Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Introduzione ai sistemi operativi

Ciao, Habr! Vorrei presentarvi una serie di articoli di traduzione su una letteratura che trovo interessante — OSTEP. In questo materiale vengono esaminati in profondità i funzionamenti dei sistemi operativi Unix-like, in particolare — il lavoro con i processi, diversi scheduler, memoria e altri componenti simili che costituiscono un moderno sistema operativo. Potete vedere l'originale di tutti i materiali qui qui. Si prega di considerare che la traduzione è stata eseguita non professionalmente (abbastanza liberamente), ma spero di aver mantenuto il significato generale.

I lavori di laboratorio su questo argomento possono essere trovati qui:

Altre parti:

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

Consideriamo l'astrazione più fondamentale che il sistema operativo fornisce agli utenti: il processo. La definizione di processo è piuttosto semplice: è un programma in esecuzione. Un programma di per sé è una cosa inanimata, situata sul disco: un insieme di istruzioni e forse alcuni dati statici, in attesa del momento di avvio. È il sistema operativo che prende questi byte e li esegue, trasformando il programma in qualcosa di utile.
Spesso gli utenti vogliono eseguire più di un programma contemporaneamente; ad esempio, puoi avviare sul tuo laptop un browser, un gioco, un lettore multimediale, un editor di testo e così via. Infatti, un sistema tipico può eseguire decine e centinaia di processi contemporaneamente. Questo fatto rende il sistema più semplice da usare; non devi mai preoccuparti se la CPU è libera, basta avviare i programmi.

Da qui deriva il problema: come garantire l'illusione di molte CPU? Come può il sistema operativo creare l'illusione di un numero praticamente infinito di CPU, anche se hai solo un CPU fisico?

Il sistema operativo crea questa illusione attraverso la virtualizzazione della CPU. Eseguendo un processo, poi fermandolo, avviando un altro processo e così via, il sistema operativo può mantenere l'illusione che esistano molte CPU virtuali, anche se in realtà ci sarà solo uno o più processori fisici. Questa tecnica è chiamata divisione delle risorse CPU nel tempo. Questa tecnica consente agli utenti di eseguire quanti più processi contemporaneamente desiderano. Il prezzo per questa soluzione è la performance: poiché se più processi condividono la CPU, ogni processo sarà elaborato più lentamente.
Per realizzare la virtualizzazione della CPU, e in particolare per farlo bene, il sistema operativo ha bisogno sia di supporto a basso livello che di supporto ad alto livello. Il supporto a basso livello si chiama meccanismi — sono metodi o protocolli a basso livello che implementano la funzionalità necessaria. Un esempio di tale funzionalità è il cambio di contesto, che consente al sistema operativo di fermare un programma e avviarne un altro sul processore. Tale divisione per tempo è implementata in tutti i moderni sistemi operativi.
Sopra questi meccanismi si trova una certa logica, incorporata nel sistema operativo, sotto forma di “politiche”. Politica — è un algoritmo di decisione del sistema operativo. Queste politiche, ad esempio, decidono quale programma eseguire (dalla lista dei comandi) per primo. Così, ad esempio, questo compito verrà risolto da una politica chiamata pianificatore (scheduling policy) e nella scelta della soluzione si baserà su dati come: storia di esecuzione (quale programma è stato eseguito più a lungo nell'ultimo minuto), quale carico genera questo processo (quali tipi di programmi sono stati eseguiti), metriche di performance (se il sistema è ottimizzato per l'interazione interattiva o per la larghezza di banda) e così via.

Astrazione: processo

L'astrazione di un programma in esecuzione, eseguito dal sistema operativo, è ciò che chiamiamo processo. Come già accennato, un processo è semplicemente un programma in esecuzione in un dato istante. Un programma attraverso il quale possiamo ottenere informazioni aggregate da varie risorse del sistema e a cui si accede o che il programma coinvolge durante la sua esecuzione.
Per capire i componenti di un processo, è necessario comprendere gli stati del sistema: ciò che il programma può leggere o modificare durante il suo funzionamento. In qualsiasi momento, è necessario capire quali elementi del sistema sono importanti per l'esecuzione del programma.
Uno degli elementi più evidenti dello stato del sistema, che include il processo, è la memoria. Le istruzioni sono memorizzate nella memoria. Anche i dati che il programma legge o scrive sono memorizzati nella memoria. Pertanto, la memoria a cui il processo può indirizzare (il cosiddetto spazio degli indirizzi) è parte del processo.
Anche i registri fanno parte dello stato del sistema. Molte istruzioni sono destinate a modificare il valore dei registri o a leggerne il valore e così i registri diventano anch'essi una parte importante del funzionamento del processo.
Vale la pena notare che lo stato della macchina è formato anche da alcuni registri speciali. Ad esempio, IP — puntatore delle istruzioni — è il puntatore all'istruzione che il programma sta eseguendo in questo momento. Ci sono anche stack pointer e il relativo frame pointer, che vengono utilizzati per gestire: parametri delle funzioni, variabili locali e indirizzi di ritorno.
Infine, i programmi accedono spesso alla ROM (memoria di sola lettura). Tali informazioni sull'“I/O” (input/output) devono includere un elenco dei file aperti dal processo in quel momento.

Process API

Per migliorare la comprensione del funzionamento del processo, esaminiamo esempi di chiamate di sistema che dovrebbero essere incluse in ogni interfaccia di un sistema operativo. Queste API, in un modo o nell'altro, sono disponibili in ogni SO.

Crea (creazione): Il sistema operativo deve includere un metodo che consenta di creare nuovi processi. Quando inserisci un comando nel terminale o avvii un'applicazione con un doppio clic sull'icona, viene inviata una richiesta al SO per creare un nuovo processo e avviare il programma specificato.
Elimina: Poiché esiste un'interfaccia per la creazione di processi, il sistema operativo deve anche fornire la possibilità di terminare forzatamente un processo. La maggior parte dei programmi naturalmente verrà avviata e terminata da sola durante la loro esecuzione. In caso contrario, l'utente vorrebbe avere la possibilità di terminarli e quindi un'interfaccia per fermare il processo non sarebbe superflua.
Aspetta (attesa): A volte è utile attendere il completamento di un processo, pertanto sono forniti alcuni interfacce che offrono la possibilità di attesa.
Controllo Vari (varia gestione): Oltre all'uccisione e all'attesa del processo, esistono diversi altri metodi di controllo. Ad esempio, la maggior parte dei sistemi operativi offre la possibilità di mettere un processo in pausa (fermandone l'esecuzione per un certo periodo) e successivamente riprendere (continuare l'esecuzione)
Stato (stato): Esistono varie interfacce per ottenere informazioni sullo stato di un processo, come la durata della sua esecuzione o in quale stato si trova attualmente.

Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Creazione del processo: dettagli

Una delle cose interessanti è come i programmi si trasformano in processi. Soprattutto, come il sistema operativo avvia e mette in esecuzione un programma. Come si crea esattamente un processo.
In primo luogo, il sistema operativo deve caricare il codice del programma e i dati statici nella memoria (nell'area di indirizzamento del processo). I programmi di solito si trovano su disco o in un'unità a stato solido in un certo formato eseguibile. Pertanto, il processo di caricamento del programma e dei dati statici in memoria richiede che il sistema operativo possa leggere questi byte dal disco e posizionarli da qualche parte nella memoria.

Nei primi sistemi operativi, il processo di caricamento veniva eseguito in modo avido (eagerly), il che significa che il codice veniva caricato in memoria completamente prima che il programma venisse eseguito. I sistemi operativi moderni lo fanno in modo pigro (lazily), caricando pezzi di codice o dati solo quando necessari dal programma durante la sua esecuzione.

Dopo che il codice e i dati statici sono stati caricati in memoria, il sistema operativo deve eseguire alcune altre operazioni prima di avviare il processo. Una certa quantità di memoria deve essere allocata per lo stack. I programmi usano lo stack per variabili locali, parametri delle funzioni e indirizzi di ritorno. Il sistema operativo alloca questa memoria e la assegna al processo. Lo stack può anche essere allocato con alcuni argomenti, in particolare riempiendo i parametri della funzione main(), ad esempio l'array argc e argv.

Il sistema operativo può anche allocare una certa quantità di memoria per l'heap del programma. L'heap è utilizzato dai programmi per dati dinamicamente allocati richiesti esplicitamente. I programmi richiedono questo spazio chiamando la funzione malloc() e lo liberano esplicitamente chiamando la funzione free(). La heap è necessaria per strutture dati come: liste collegate, tabelle hash, alberi e altro. All'inizio, viene allocata una piccola quantità di memoria per la heap, ma nel corso del tempo, mentre il programma è in esecuzione, la heap può richiedere una maggiore quantità di memoria tramite la chiamata di API malloc(). Il sistema operativo è coinvolto nel processo di allocazione di più memoria per aiutare a soddisfare queste richieste.

Il sistema operativo eseguirà anche compiti di inizializzazione, in particolare quelli relativi all'input-output. Ad esempio, nei sistemi UNIX, ogni processo ha di default 3 descrittori di file aperti per il flusso standard di input, output e errori. Questi descrittori consentono ai programmi di leggere l'input dal terminale e di visualizzare informazioni sullo schermo.

Così, caricando il codice e i dati statici in memoria, creando e inizializzando lo stack e svolgendo altri compiti relativi all'esecuzione delle operazioni di input-output, il sistema operativo prepara il terreno per l'esecuzione del processo. Alla fine, rimane l'ultimo compito: avviare l'esecuzione del programma attraverso il suo punto di ingresso, chiamato funzione main(). Procedendo all'esecuzione della funzione main(), il sistema operativo trasferisce il controllo della CPU al nuovo processo creato, e così il programma inizia a essere eseguito.

Stato del processo

Ora che abbiamo una comprensione di cosa sia un processo e come venga creato, elenchiamo gli stati del processo in cui può trovarsi. Nella sua forma più semplice, un processo può trovarsi in uno di questi stati:
Esecuzione. Nel stato di esecuzione, il processo è in esecuzione sulla CPU. Ciò significa che le istruzioni vengono eseguite.
Pronto. Nel stato di attesa, il processo è pronto ad avviarsi, ma per qualche motivo il sistema operativo non lo esegue in quel preciso momento.
Bloccato. Nel stato bloccato, il processo sta eseguendo alcune operazioni che non gli consentono di essere pronto per l'esecuzione fino a quando non si verifica un certo evento. Un esempio comune è quando un processo inizia un'operazione di IO, diventa bloccato e così un altro processo può utilizzare la CPU.

Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Possiamo immaginare questi stati come un grafo. Come possiamo vedere nell'immagine, lo stato del processo può cambiare tra RUNNING e READY a discrezione del sistema operativo. Quando lo stato del processo cambia da READY a RUNNING, significa che il processo è stato pianificato. Al contrario, viene rimosso dalla pianificazione. Nel momento in cui il processo diventa BLOCKED, ad esempio, inizializzando un'operazione IO, il sistema operativo lo terrà in questo stato fino al verificarsi di un certo evento, come la conclusione dell'IO. In questo momento avviene il passaggio allo stato READY e possibilmente immediatamente allo stato RUNNING, se così decide il sistema operativo.
Guardiamo un esempio di come due processi attraversano questi stati. Per iniziare, immaginiamo che entrambi i processi siano in esecuzione e ognuno utilizzi solo la CPU. In questo caso, i loro stati appariranno come segue.

Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Nell'esempio seguente, il primo processo dopo un certo tempo di lavoro richiede IO e passa allo stato BLOCKED, dando l'opportunità di avviare un altro processo (FIG 1.4). Il sistema operativo vede che il processo 0 non utilizza la CPU e avvia il processo 1. Durante l'esecuzione del processo 1, l'IO termina e lo stato del processo 0 cambia in READY. Infine, il processo 1 termina, e al termine di questo, il processo 0 viene avviato, eseguito e completa il suo lavoro.

Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Struttura dei dati

Il sistema operativo è esso stesso un programma e, come qualsiasi altro programma, ha alcune strutture dati chiave che tracciano vari pezzi di informazioni rilevanti. Per monitorare lo stato di ogni processo, il sistema operativo manterrà una certa process list per tutti i processi nello stato READY e alcune informazioni aggiuntive per monitorare i processi che vengono eseguiti in quel momento. Inoltre, il sistema operativo deve monitorare i processi bloccati. Dopo la conclusione dell'IO, il sistema operativo deve risvegliare il processo necessario e portarlo in uno stato pronto per l'esecuzione.

Ad esempio, il sistema operativo deve salvare lo stato dei registri della CPU. Nel momento in cui il processo viene arrestato, lo stato dei registri viene salvato nello spazio indirizzabile del processo, e nel momento in cui il suo lavoro riprende — ripristinare i valori dei registri e così proseguire nell'esecuzione di quel processo.

Oltre agli stati di ready, blocked e running, esistono anche altri stati. A volte, al momento della creazione, un processo può trovarsi nello stato INIT. Infine, un processo può essere posto nello stato FINAL, quando è già terminato, ma le sue informazioni non sono ancora state rimosse. Nei sistemi UNIX, questo stato è chiamato processo-zombi. Questo stato è utile nei casi in cui il processo genitore desidera conoscere il codice di ritorno del figlio; ad esempio, di solito 0 segnala una conclusione riuscita, mentre 1 indica un errore, ma i programmatori possono utilizzare codici di uscita aggiuntivi segnalando vari problemi. Alla conclusione, il processo genitore effettua l'ultima chiamata di sistema, come wait(), per attendere la conclusione del lavoro del processo figlio e segnalare al sistema operativo che è possibile ripulire qualsiasi dato associato al processo concluso.

Sistemi Operativi: Tre Pezzi Facili. Parte 2: Astrazione: Processo (traduzione)

Punti chiave della lezione:

Processo è l'astrazione principale di un programma in esecuzione in un sistema operativo. In qualsiasi momento, un processo può essere descritto in base al suo stato: il contenuto della memoria nel suo spazio degli indirizzi, il contenuto dei registri del processore, compresi il puntatore delle istruzioni e il puntatore dello stack, così come informazioni sugli IO, come i file aperti che vengono letti o scritti.
Process API è composto da chiamate che i programmi possono effettuare riguardo ai processi. Di solito, queste sono chiamate di creazione, eliminazione o altre.
● Un processo si trova in uno dei tanti stati, inclusi running, ready, blocked. Diversi eventi, come la pianificazione, l'uscita dalla pianificazione o l'attesa, possono cambiare lo stato del processo da uno a un altro.
Elenco dei processi contiene informazioni su tutti i processi nel sistema. Ogni voce in esso è chiamata process control block, che è in realtà una struttura che contiene tutte le informazioni necessarie su un processo specifico. 

Fonte: habr.com

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