GNU/Linux >> Linux Esercitazione >  >> Linux

Perdita di tempo di execv() e fork()

Non più. C'è qualcosa chiamato COW (Copy On Write), solo quando uno dei due processi (Padre/Figlio) tenta di scrivere su un dato condiviso, questo viene copiato.

In passato:
Il fork() la chiamata di sistema ha copiato lo spazio degli indirizzi del processo chiamante (il genitore) per creare un nuovo processo (il figlio). La copia dello spazio degli indirizzi del genitore nel figlio è stata la parte più costosa del fork() funzionamento.

Adesso:
Una chiamata a fork() è spesso seguito quasi immediatamente da una chiamata a exec() nel processo figlio, che sostituisce la memoria del bambino con un nuovo programma. Questo è ciò che fa tipicamente la shell, per esempio. In questo caso, il tempo speso a copiare lo spazio degli indirizzi del genitore è in gran parte sprecato, perché il processo figlio utilizzerà pochissima memoria prima di chiamare exec() .

Per questo motivo, le versioni successive di Unix hanno sfruttato l'hardware della memoria virtuale per consentire al genitore e al figlio di condividere la memoria mappata nei rispettivi spazi degli indirizzi fino a quando uno dei processi non la modifica effettivamente. Questa tecnica è nota come copia su scrittura . Per farlo, su fork() il kernel copierebbe le mappature dello spazio degli indirizzi dal genitore al figlio invece del contenuto delle pagine mappate e allo stesso tempo contrassegnerebbe le pagine ora condivise come di sola lettura. Quando uno dei due processi tenta di scrivere su una di queste pagine condivise, il processo rileva un errore di pagina. A questo punto, il kernel Unix si rende conto che la pagina era davvero una copia "virtuale" o "copia su scrittura", e quindi crea una nuova copia privata e scrivibile della pagina per il processo di errore. In questo modo, i contenuti delle singole pagine non vengono effettivamente copiati fino a quando non vengono effettivamente scritti. Questa ottimizzazione fa un fork() seguito da un exec() nel bambino molto più economico:il bambino probabilmente dovrà solo copiare una pagina (la pagina corrente del suo stack) prima di chiamare exec() .


Qual è il vantaggio che si ottiene utilizzando questa combinazione (invece di qualche altra soluzione) che fa sì che le persone continuino a usarla anche se abbiamo dei rifiuti?

Devi creare un nuovo processo in qualche modo. Ci sono pochissimi modi in cui un programma in spazio utente può farlo. POSIX aveva vfork() accanto a fork() e alcuni sistemi potrebbero avere i propri meccanismi, come clone() specifico per Linux , ma dal 2008 POSIX specifica solo fork() e il posix_spawn() famiglia. Il fork + exec il percorso è più tradizionale, è ben compreso e presenta pochi inconvenienti (vedi sotto). Il posix_spawn la famiglia è concepita come uno scopo speciale sostituire l'uso in contesti che presentano difficoltà per fork(); puoi trovare i dettagli nella sezione "Rationale" delle sue specifiche.

Questo estratto dalla pagina man di Linux per vfork() può essere illuminante:

Sotto Linux, fork (2) è implementato utilizzando pagine copy-on-write, quindi l'unica penalità subita da fork (2) è il tempo e la memoria necessari per duplicare le tabelle delle pagine del genitore e per creare una struttura di attività univoca per il bambino . Tuttavia, ai vecchi tempi un fork (2) richiederebbe di fare una copia completa dello spazio dati del chiamante, spesso inutilmente, poiché di solito subito dopo un exec (3) è fatto. Così, per una maggiore efficienza, BSD ha introdotto il vfork () chiamata di sistema, che non copiava completamente lo spazio degli indirizzi del processo genitore, ma prendeva in prestito la memoria e il thread di controllo del genitore fino a una chiamata a execve (2) o si è verificata un'uscita. Il processo padre è stato sospeso mentre il figlio ne utilizzava le risorse. L'uso di vfork () era complicato:ad esempio, non modificare i dati nel processo padre dipendeva dal sapere quali variabili sono contenute in un registro.

(Enfasi aggiunta)

Pertanto, la tua preoccupazione per lo spreco non è fondata per i sistemi moderni (non limitati a Linux), ma storicamente era davvero un problema e c'erano davvero meccanismi progettati per evitarlo. Al giorno d'oggi, la maggior parte di questi meccanismi è obsoleta.


Un'altra risposta afferma:

Tuttavia, ai vecchi tempi un fork(2) richiedeva la creazione di una copia completa dello spazio dati del chiamante, spesso inutilmente, poiché di solito subito dopo viene eseguito un exec(3).

Ovviamente, i brutti vecchi tempi di una persona sono molto più giovani di quanto gli altri ricordino.

I sistemi UNIX originali non avevano la memoria per l'esecuzione di più processi e non avevano una MMU per mantenere diversi processi nella memoria fisica pronti per l'esecuzione nello stesso spazio di indirizzi logici:hanno scambiato i processi su disco che non era attualmente in esecuzione.

La chiamata di sistema fork era quasi interamente uguale allo scambio del processo corrente su disco, ad eccezione del valore di ritorno e di not sostituendo la copia in memoria rimanente scambiando in un altro processo. Dato che dovevi comunque scambiare il processo genitore per eseguire il figlio, fork+exec non comportava alcun sovraccarico.

È vero che c'è stato un periodo di tempo in cui fork+exec era imbarazzante:quando c'erano MMU che fornivano una mappatura tra lo spazio degli indirizzi logico e fisico, ma i page fault non conservavano abbastanza informazioni che il copy-on-write e una serie di altri virtual address -memory/demand-paging erano fattibili.

Questa situazione era abbastanza dolorosa, non solo per UNIX, che la gestione degli errori di pagina dell'hardware è stata adattata per diventare "riproducibile" abbastanza velocemente.


Linux
  1. Sostituzione del processo e tubo?

  2. Ora di inizio del processo con fuso orario?

  3. Processi Linux:ID processo, funzioni C fork, execv, wait, waitpid

  4. Stati del processo Linux

  5. Differenza tra CLOCK_REALTIME e CLOCK_MONOTONIC?

Server NTP e best practice

Come sospendere un processo e riprenderlo in un secondo momento in Linux

Come impostare data, ora e fuso orario in RHEL 8

Come trovare la data e l'ora di installazione del sistema operativo Linux

Come impostare data e ora su Linux

timestamp, ora di modifica e ora di creazione di un file