Discussioni vs processi in Linux


253

Di recente ho sentito alcune persone dire che in Linux è quasi sempre meglio usare i processi anziché i thread, poiché Linux è molto efficiente nella gestione dei processi e perché ci sono così tanti problemi (come il blocco) associati ai thread. Tuttavia, sono sospettoso, perché sembra che i thread possano dare un notevole aumento delle prestazioni in alcune situazioni.

Quindi la mia domanda è, di fronte a una situazione che thread e processi potrebbero gestire abbastanza bene, dovrei usare processi o thread? Ad esempio, se stavo scrivendo un server Web, dovrei usare processi o thread (o una combinazione)?


C'è una differenza con Linux 2.4?
mouviciel,

3
La differenza tra processi e thread in Linux 2.4 è che i thread condividono più parti del loro stato (spazio degli indirizzi, handle di file, ecc.) Rispetto ai processi, che di solito non lo fanno. L'NPTL sotto Linux 2.6 lo rende un po 'più chiaro dando loro "gruppi di thread" che sono un po' come i "processi" in win32 e Solaris.
Mark R

6
La programmazione concorrente è difficile. A meno che tu non abbia bisogno di prestazioni molto elevate, l'aspetto più importante del tuo compromesso sarà spesso la difficoltà del debug . I processi rendono la soluzione molto più semplice in questo senso, poiché tutte le comunicazioni sono esplicite (facile da controllare, registrare ecc.). Al contrario, la memoria condivisa dei thread crea gazillions di luoghi in cui un thread può avere un impatto errato su un altro.
Lutz Prechelt,

1
@LutzPrechelt - La programmazione simultanea può essere multi-thread e multi-processo. Non vedo perché stai presupponendo che la programmazione concorrente sia solo multi thread. Potrebbe essere a causa di alcune limitazioni linguistiche particolari, ma in generale può essere entrambe le cose.
iankit,

2
Collegamento Lutz ha semplicemente affermato che la programmazione concorrente è difficile a prescindere dalla scelta - processo o thread - ma che la programmazione concorrente che utilizza processi rende il debug più semplice in molti casi.
user2692263,

Risposte:


322

Linux usa un modello di threading 1-1, con (per il kernel) nessuna distinzione tra processi e thread - tutto è semplicemente un'attività eseguibile. *

Su Linux, la chiamata di sistema cloneclona un'attività, con un livello configurabile di condivisione, tra cui:

  • CLONE_FILES: condividi la stessa tabella descrittore di file (invece di creare una copia)
  • CLONE_PARENT: non impostare una relazione genitore-figlio tra la nuova attività e la vecchia (altrimenti, child's getppid()= parent's getpid())
  • CLONE_VM: condividi lo stesso spazio di memoria (invece di creare una copia COW )

fork()chiama la clone(condivisione minima )e pthread_create()chiama la clone(condivisione più ). **

forking costa un po 'di più che pthread_createing a causa della copia di tabelle e della creazione di mapping COW per la memoria, ma gli sviluppatori del kernel Linux hanno provato (e sono riusciti) a ridurre al minimo tali costi.

Il passaggio da un'attività all'altra, se condividono lo stesso spazio di memoria e varie tabelle, sarà un po 'più economico rispetto a se non sono condivisi, perché i dati potrebbero già essere caricati nella cache. Tuttavia, il cambio di attività è ancora molto veloce anche se nulla è condiviso: questo è qualcos'altro che gli sviluppatori del kernel Linux cercano di garantire (e riescono a garantire).

In effetti, se si utilizza un sistema multiprocessore, la mancata condivisione può effettivamente essere utile per le prestazioni: se ogni attività è in esecuzione su un processore diverso, la sincronizzazione della memoria condivisa è costosa.


* Semplificato. CLONE_THREADfa sì che la consegna dei segnali sia condivisa (cosa necessaria CLONE_SIGHAND, che condivide la tabella del gestore dei segnali).

** Semplificato. Esistono entrambi SYS_forke SYS_clonesyscalls, ma nel kernel, sys_forke sys_clonesono entrambi wrapper molto sottili attorno alla stessa do_forkfunzione, che a sua volta è un wrapper sottile copy_process. Sì, i termini process, threade tasksono usati in modo intercambiabile piuttosto nel kernel di Linux ...


6
Penso che ci manchi 1 punto. Se si eseguono più processi per il proprio server Web, è necessario scrivere un altro processo per aprire il socket e passare "lavoro" a thread diversi. Il threading offre un singolo processo a thread multipli, design pulito. In molte situazioni il thread è semplicemente naturale e in altre situazioni un nuovo processo è semplicemente naturale. Quando il problema cade in una zona grigia, gli altri compromessi, come spiegato dall'ephemient, diventano importanti.
Saurabh,

26
@Saurabh Non proprio. Si può facilmente socket, bind, listen, fork, e quindi avere più processi acceptconnessioni sullo stesso socket di ascolto. Un processo può smettere di accettare se è occupato e il kernel instraderà le connessioni in entrata verso un altro processo (se nessuno è in ascolto, il kernel accoderà o lascerà cadere, a seconda del listenbacklog). Non hai molto più controllo sulla distribuzione del lavoro di così, ma di solito è abbastanza buono!
effimero

2
@Bloodcount Tutti i processi / thread su Linux sono creati dallo stesso meccanismo, che clona un processo / thread esistente. Bandiere passate per clone()determinare quali risorse sono condivise. Un'attività può anche unshare()risorse in qualsiasi momento successivo.
effimero

4
@KarthikBalaguru All'interno del kernel stesso, c'è un task_structper ogni attività. Questo è spesso chiamato "processo" in tutto il codice del kernel, ma corrisponde a ciascun thread eseguibile. Non c'è process_struct; se un gruppo di messaggi di posta task_structelettronica sono collegati insieme dal loro thread_groupelenco, sono lo stesso "processo" nello spazio utente. C'è un po 'di gestione speciale dei "thread", ad es. Tutti i thread di pari livello vengono arrestati su fork e exec e viene visualizzato solo il thread "principale" ls /proc. Ogni thread è accessibile tramite /proc/pid, sia che sia elencato /proco meno.
effimero

5
@KarthikBalaguru Il kernel supporta un continuum di comportamento tra thread e processi; per esempio, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))ti darebbe un nuovo "thread" che non condivide directory di lavoro, file o blocchi, mentre clone(CLONE_FILES | CLONE_FS | CLONE_IO)ti darebbe un "processo" che lo fa. Il sistema sottostante crea attività clonando; fork()e pthread_create()sono solo funzioni di libreria che invocano clone()diversamente (come ho scritto in questa risposta).
effimero

60

Linux (e in effetti Unix) ti offre una terza opzione.

Opzione 1 - processi

Crea un eseguibile autonomo che gestisca una parte (o tutte le parti) dell'applicazione e invocalo separatamente per ogni processo, ad esempio il programma esegue copie di se stesso per delegare le attività.

Opzione 2 - discussioni

Crea un eseguibile autonomo che si avvia con un singolo thread e crea thread aggiuntivi per eseguire alcune attività

Opzione 3 - forcella

Disponibile solo su Linux / Unix, questo è un po 'diverso. Un processo biforcato è in realtà un proprio processo con un proprio spazio di indirizzi - non c'è nulla che il bambino possa fare (normalmente) per influenzare lo spazio di indirizzi dei suoi genitori o fratelli (a differenza di un thread) - quindi si ottiene una maggiore robustezza.

Tuttavia, le pagine di memoria non vengono copiate, sono copia su scrittura, quindi di solito viene utilizzata meno memoria di quanto si possa immaginare.

Prendi in considerazione un programma per server Web che consta di due passaggi:

  1. Leggi i dati di configurazione e di runtime
  2. Servire le richieste di pagina

Se hai utilizzato i thread, il passaggio 1 verrebbe eseguito una volta e il passaggio 2 in più thread. Se sono stati utilizzati processi "tradizionali", è necessario ripetere i passaggi 1 e 2 per ciascun processo e duplicare la memoria per memorizzare i dati di configurazione e di runtime. Se hai usato fork (), puoi eseguire il passaggio 1 una volta, quindi fork (), lasciando i dati di runtime e la configurazione in memoria, intatti, non copiati.

Quindi ci sono davvero tre scelte.


7
Il biforcazione di @Qwertie non è così bello, rompe molte librerie in modi sottili (se le usi nel processo padre). Crea comportamenti inaspettati che confondono anche i programmatori esperti.
Mark R

2
@MarkR potresti fornire alcuni esempi o un link su come il fork di forking rompe la libreria e crea comportamenti inaspettati?
Ehtesh Choudhury,

18
Se un processo esegue il fork con una connessione mysql aperta, possono verificarsi problemi, poiché il socket è condiviso tra due processi. Anche se solo un processo utilizza la connessione, l'altro ne impedisce la chiusura.
Mark

1
fork () la chiamata di sistema è specificata da POSIX (il che significa che è disponibile su qualsiasi sistema Unix), se hai usato l'API Linux sottostante, che è la chiamata di sistema clone (), allora in realtà hai ancora più scelte in Linux che solo le tre .
Lie Ryan,

2
@MarkR La condivisione del socket è di progettazione. Inoltre, uno dei processi può chiudere il socket usando linux.die.net/man/2/shutdown prima di chiamare close () sul socket.
Lelanthran,

53

Dipende da molti fattori. I processi sono più pesanti dei thread e hanno un costo di avvio e spegnimento più elevato. La comunicazione tra processi (IPC) è anche più difficile e più lenta della comunicazione interthread.

Al contrario, i processi sono più sicuri e più sicuri dei thread, poiché ogni processo viene eseguito nel proprio spazio di indirizzi virtuale. Se un processo si arresta in modo anomalo o ha un sovraccarico del buffer, non influisce affatto su nessun altro processo, mentre se un thread si arresta in modo anomalo, elimina tutti gli altri thread nel processo e se un thread ha un sovraccarico del buffer, si apre un buco di sicurezza in tutti i thread.

Pertanto, se i moduli dell'applicazione possono essere eseguiti principalmente in modo indipendente con poca comunicazione, è consigliabile utilizzare i processi se è possibile permettersi i costi di avvio e spegnimento. Il successo prestazionale di IPC sarà minimo e sarai leggermente più sicuro contro bug e falle nella sicurezza. Se hai bisogno di tutte le prestazioni che puoi ottenere o avere molti dati condivisi (come strutture di dati complessi), vai con i thread.


9
La risposta di Adam sarebbe utile come briefing esecutivo. Per maggiori dettagli, MarkR ed effimero forniscono buone spiegazioni. Una spiegazione molto dettagliata con esempi può essere trovata su cs.cf.ac.uk/Dave/C/node29.html ma sembra essere un po 'datato in alcune parti.
CyberFonic,

2
CyberFonic è vero per Windows. Come dice l'effimero in Linux i processi non sono più pesanti. E sotto Linux tutti i meccanismi disponibili per la comunicazione tra thread (futex, memoria condivisa, pipe, IPC) sono anche disponibili per i processi e funzionano alla stessa velocità.
Russell Stuart,

L'IPC è più difficile da usare, ma cosa succede se qualcuno usa la "memoria condivisa"?
abhiarora,

11

Altri hanno discusso delle considerazioni.

Forse la differenza importante è che nei processi di Windows sono pesanti e costosi rispetto ai thread, e in Linux la differenza è molto più piccola, quindi l'equazione si bilancia in un punto diverso.


9

C'era una volta Unix e in questa buona vecchia Unix c'erano molti costi generali per i processi, quindi quello che alcune persone intelligenti hanno fatto è stato creare thread, che avrebbero condiviso lo stesso spazio di indirizzi con il processo padre e avevano solo bisogno di un contesto ridotto switch, che renderebbe il cambio di contesto più efficiente.

In un Linux contemporaneo (2.6.x) non c'è molta differenza nelle prestazioni tra un cambio di contesto di un processo rispetto a un thread (solo il materiale MMU è aggiuntivo per il thread). C'è un problema con lo spazio degli indirizzi condiviso, il che significa che un puntatore difettoso in un thread può corrompere la memoria del processo padre o un altro thread all'interno dello stesso spazio indirizzo.

Un processo è protetto dalla MMU, quindi un puntatore difettoso causerà solo un segnale 11 e nessuna corruzione.

In generale utilizzerei i processi (non molto overover del contesto in Linux, ma protezione della memoria a causa della MMU), ma se dovessi avere bisogno di una classe scheduler in tempo reale, che è una tazza di tè diversa tutti insieme.

Perché pensi che i thread abbiano un così grande guadagno in termini di prestazioni su Linux? Hai dei dati per questo o è solo un mito?


1
Sì, ho alcuni dati. Ho eseguito un test che crea 100.000 processi e un test che crea 100.000 thread. La versione del thread ha funzionato circa 9 volte più velocemente (17,38 secondi per i processi, 1,93 per i thread). Ora questo verifica solo i tempi di creazione, ma per attività di breve durata, i tempi di creazione possono essere fondamentali.
user17918

4
@ user17918 - È possibile condividere il codice da te utilizzato per calcolare i tempi sopra indicati.
codingfreak

una grande differenza, con i processi il kernel crea una tabella delle pagine per ogni processo e le thead usano solo una tabella delle pagine, quindi penso sia normale che i thread siano più veloci dei processi
c4f4t0r

Un altro modo semplice per vederlo è TCB è piuttosto più piccolo del PCB e quindi è ovvio che il cambio di contesto di processo che coinvolge PCB consumerà un po 'più di tempo rispetto a quello del cambio di thread.
Karthik Balaguru,

5

Quanto sono strettamente collegati i tuoi compiti?

Se possono vivere indipendentemente l'uno dall'altro, quindi utilizzare i processi. Se si basano l'uno sull'altro, utilizzare i thread. In questo modo è possibile interrompere e riavviare un processo errato senza interferire con il funzionamento delle altre attività.


4

A complicare ulteriormente le cose, esiste qualcosa come l'archiviazione locale dei thread e la memoria condivisa di Unix.

L'archiviazione locale di thread consente a ciascun thread di avere un'istanza separata di oggetti globali. L'unica volta che l'ho usato è stato durante la costruzione di un ambiente di emulazione su Linux / Windows, per il codice dell'applicazione eseguito in un RTOS. In RTOS ogni attività era un processo con il proprio spazio di indirizzi, nell'ambiente di emulazione ogni attività era un thread (con uno spazio di indirizzi condiviso). Usando TLS per cose come i singleton, siamo stati in grado di avere un'istanza separata per ogni thread, proprio come nel "vero" ambiente RTOS.

La memoria condivisa può (ovviamente) offrire i vantaggi in termini di prestazioni di avere più processi che accedono alla stessa memoria, ma a costo / rischio di dover sincronizzare correttamente i processi. Un modo per farlo è quello di avere un processo creare una struttura di dati nella memoria condivisa e quindi inviare un handle a quella struttura tramite la comunicazione tra processi tradizionale (come una pipa denominata).


1
Ho usato l'archiviazione locale di thread per una raccolta di statistiche, l'ultima volta che stavo scrivendo un programma di reti thread: ogni thread scriveva nei propri contatori, non era necessario alcun blocco e solo quando veniva inviato un messaggio ogni thread combinava le sue statistiche con i totali globali. Sì, TLS non è molto comunemente usato o necessario. Memoria condivisa, d'altra parte ... oltre a inviare in modo efficiente i dati, è anche possibile condividere semafori POSIX tra processi inserendoli nella memoria condivisa. È piuttosto sorprendente.
effimero

4

Nel mio recente lavoro con LINUX è una cosa di cui tenere conto sono le librerie. Se si utilizzano thread assicurarsi che tutte le librerie che è possibile utilizzare tra thread siano thread-safe. Questo mi ha bruciato un paio di volte. In particolare libxml2 non è thread-out pronto all'uso. Può essere compilato con thread sicuro ma non è quello che ottieni con aptitude install.


3

Dovrei essere d'accordo con quello che hai sentito. Quando eseguiamo il benchmarking del nostro cluster ( xhple simili), otteniamo sempre prestazioni significativamente migliori con processi su thread.</anecdote>


3

La decisione tra thread / processo dipende un po 'da cosa lo userete. Uno dei vantaggi di un processo è che ha un PID e può essere ucciso senza interrompere anche il genitore.

Per un esempio reale di un server Web, apache 1.3 supportava solo più processi, ma in 2.0 hanno aggiunto un'astrazione in modo da poter passare da uno all'altro. I commenti sembrano concordare sul fatto che i processi sono più robusti, ma i thread possono offrire prestazioni leggermente migliori (ad eccezione di Windows in cui le prestazioni per i processi fanno schifo e si desidera utilizzare solo i thread).


2

Per la maggior parte dei casi preferirei i processi rispetto ai thread. i thread possono essere utili quando si ha un'attività relativamente più piccola (overhead del processo >> tempo impiegato da ciascuna unità operativa divisa) e c'è bisogno di una condivisione della memoria tra di loro. Pensa a una vasta gamma. Inoltre (offtopic), si noti che se l'utilizzo della CPU è pari o vicino al 100 percento, il multithreading o l'elaborazione non trarranno alcun vantaggio. (infatti peggiorerà)


Cosa vuoi dire senza beneficio? Che ne dici di eseguire calcoli pesanti nel thread della GUI? Spostarli su thread paralleli sarà molto migliore dal punto di vista dell'esperienza dell'utente, indipendentemente dal modo in cui viene caricata la CPU.
olegst

2

Discussioni -> Le discussioni condividono uno spazio di memoria, è un'astrazione della CPU, è leggera. Processi -> I processi hanno il loro spazio di memoria, è un'astrazione di un computer. Per parallelizzare l'attività è necessario astrarre una CPU. Tuttavia, i vantaggi dell'utilizzo di un processo su un thread sono la sicurezza, la stabilità mentre un thread utilizza meno memoria rispetto al processo e offre minore latenza. Un esempio in termini di web sarebbe Chrome e Firefox. Nel caso di Chrome ogni scheda è un nuovo processo, quindi l'utilizzo della memoria di Chrome è superiore a Firefox, mentre la sicurezza e la stabilità fornite sono migliori di Firefox. La sicurezza qui fornita da Chrome è migliore, poiché ogni scheda è un nuovo processo che una scheda diversa non può curiosare nello spazio di memoria di un determinato processo.


2

Penso che tutti abbiano fatto un ottimo lavoro nel rispondere alla tua domanda. Sto solo aggiungendo ulteriori informazioni sul thread rispetto al processo in Linux per chiarire e riassumere alcune delle risposte precedenti nel contesto del kernel. Quindi, la mia risposta riguarda il codice specifico del kernel in Linux. Secondo la documentazione del kernel Linux, non esiste una chiara distinzione tra thread e processo, tranne che il thread utilizza lo spazio di indirizzi virtuali condivisi a differenza del processo. Si noti inoltre che il kernel Linux utilizza il termine "task" per fare riferimento a processo e thread in generale.

"Non ci sono strutture interne che implementano processi o thread, invece c'è una struttura task_struct che descrive un'unità di pianificazione astratta chiamata task"

Inoltre, secondo Linus Torvalds, NON dovresti assolutamente pensare al processo rispetto al thread e perché è troppo limitante e l'unica differenza è COE o Context of Execution in termini di "separare lo spazio degli indirizzi dal genitore" o lo spazio degli indirizzi condiviso. In effetti usa un esempio di web server per chiarire qui (che consiglio vivamente di leggere).

Credito completo per la documentazione del kernel linux


-3

Se hai bisogno di condividere risorse, dovresti davvero usare i thread.

Considera anche il fatto che i cambi di contesto tra thread sono molto meno costosi dei cambi di contesto tra processi.

Non vedo alcun motivo per passare esplicitamente a processi separati a meno che tu non abbia una buona ragione per farlo (sicurezza, test delle prestazioni comprovati, ecc ...)


3
Ho il rappresentante da modificare, ma non sono del tutto d'accordo. Il cambio di contesto tra processi su Linux è quasi economico quanto il cambio di contesto tra thread.
effimero
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.