fork()
era la chiamata di sistema UNIX originale. Può essere utilizzato solo per creare nuovi processi, non thread. Inoltre, è portatile.
In Linux, clone()
è una nuova e versatile chiamata di sistema che può essere utilizzata per creare un nuovo thread di esecuzione. A seconda delle opzioni passate, il nuovo thread di esecuzione può aderire alla semantica di un processo UNIX, un thread POSIX, una via di mezzo o qualcosa di completamente diverso (come un contenitore diverso). Puoi specificare tutti i tipi di opzioni che stabiliscono se la memoria, i descrittori di file, i vari spazi dei nomi, i gestori di segnale e così via vengono condivisi o copiati.
Dal clone()
è la chiamata di sistema superset, l'implementazione del fork()
il wrapper delle chiamate di sistema in glibc chiama effettivamente clone()
, ma questo è un dettaglio di implementazione che i programmatori non devono conoscere. Il vero fork()
la chiamata di sistema esiste ancora nel kernel di Linux per ragioni di compatibilità con le versioni precedenti, anche se è diventata ridondante, perché i programmi che utilizzano versioni molto vecchie di libc, o un'altra libc oltre a glibc, potrebbero utilizzarla.
clone()
è anche usato per implementare il pthread_create()
Funzione POSIX per la creazione di thread.
I programmi portabili dovrebbero chiamare fork()
e pthread_create()
, non clone()
.
Sembra che ci siano due clone()
cose che circolano in Linux 2.6
C'è una chiamata di sistema:
int clone(int (*fn)(void *), void *child_stack,
int flags, void *arg, ...
/* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
Questo è il "clone()" descritto facendo man 2 clone
.
Se leggi quella pagina man abbastanza da vicino, vedrai questo:
It is actually a library function layered on top of the
underlying clone() system call.
Apparentemente, dovresti implementare il threading usando la "funzione di libreria" sovrapposta alla chiamata di sistema dal nome identico e confuso.
Ho scritto un breve programma:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
pid_t cpid;
switch (cpid = fork()) {
case 0: // Child process
break;
case -1: // Error
break;
default: // parent process
break;
}
return 0;
}
Compilato con:c99 -Wall -Wextra
e l'ho eseguito in strace -f
per vedere cosa fanno effettivamente le chiamate di sistema che si diramano. L'ho preso da strace
su una macchina Linux 2.6.18 (CPU x86_64):
20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0) = ?
20098 exit_group(0)
Nessuna chiamata "fork" appare nel strace
produzione. Il clone()
chiamata che compare nel strace
output ha argomenti molto diversi da man-page-clone. child_stack=0
poiché il primo argomento è diverso da int (*fn)(void *)
.
Sembra che il file fork(2)
la chiamata di sistema è implementata in termini di reale clone()
, proprio come la "funzione libreria" clone()
è implementato. Il reale clone()
ha un diverso set di argomenti rispetto a man-page-clone.
Semplificando, entrambe le tue affermazioni apparentemente contraddittorie su fork()
e clone()
sono corretti. Il "clone" coinvolto è diverso, però.
fork()
è solo un particolare insieme di flag per la chiamata di sistema clone()
. clone()
è abbastanza generale da creare un "processo" o un "thread" o anche cose strane che si trovano da qualche parte tra processi e thread (ad esempio, diversi "processi" che condividono la stessa tabella di descrittori di file).
Essenzialmente, per ogni "tipo" di informazione associata a un contesto di esecuzione nel kernel, clone()
ti dà la scelta di aliasare tali informazioni o copiarle. I thread corrispondono all'aliasing, i processi corrispondono alla copia. Specificando combinazioni intermedie di flag a clone()
, puoi creare cose strane che non sono thread o processi. Di solito non dovresti farlo, e immagino ci sia stato qualche dibattito durante lo sviluppo del kernel Linux sul fatto che dovrebbe consentire un meccanismo generale come clone()
.