risposta-alla-domanda-sullo-sviluppo-web-bd.com

La differenza tra fork (), vfork (), exec () e clone ()

Stavo cercando di trovare la differenza tra questi quattro su Google e mi aspettavo che ci fosse un'enorme quantità di informazioni su questo, ma in realtà non c'era nessun solido confronto tra le quattro chiamate.

Ho iniziato a provare a compilare una sorta di sguardo d'insieme sulle differenze tra queste chiamate di sistema ed ecco cosa ho ottenuto. Tutte queste informazioni sono corrette/mi sto perdendo qualcosa di importante?

Fork: la chiamata fork sostanzialmente fa un duplicato del processo corrente, identico in quasi tutti i modi (non tutto viene copiato, ad esempio, i limiti delle risorse in alcune implementazioni, ma l'idea è quella di creare una copia il più vicino possibile).

Il nuovo processo (figlio) ottiene un diverso ID processo (PID) e ha il PID del vecchio processo (padre) come PID padre (PPID). Poiché ora i due processi eseguono esattamente lo stesso codice, possono stabilire quale sia il codice di ritorno del fork: il figlio ottiene 0, il genitore ottiene il PID del figlio. Tutto questo, ovviamente, presupponendo che la chiamata fork funzioni - in caso contrario, non viene creato alcun figlio e il genitore riceve un codice di errore.

Vfork: La differenza di base tra vfork e fork è che quando viene creato un nuovo processo con vfork (), il processo parent viene temporaneamente sospeso e il processo child potrebbe prendere in prestito lo spazio degli indirizzi del parent. Questo strano stato di cose continua fino a quando non termina il processo figlio o chiama execve (), a quel punto il processo genitore continua.

Ciò significa che il processo figlio di un vfork () deve fare attenzione per evitare di modificare in modo imprevisto le variabili del processo genitore. In particolare, il processo figlio non deve tornare dalla funzione contenente la chiamata vfork () e non deve chiamare exit () (se deve uscire, dovrebbe usare _exit (); in realtà, questo vale anche per il figlio di una forcella normale ()).

Exec : La chiamata exec è un modo per sostituire sostanzialmente l'intero processo corrente con un nuovo programma. Carica il programma nello spazio del processo corrente e lo esegue dal punto di ingresso. exec () sostituisce il processo corrente con un eseguibile indicato dalla funzione. Il controllo non ritorna mai al programma originale a meno che non ci sia un errore exec ().

Clone : Il clone, come fork, crea un nuovo processo. A differenza del fork, queste chiamate consentono al processo figlio di condividere parti del suo contesto di esecuzione con il processo chiamante, come lo spazio di memoria, la tabella dei descrittori di file e la tabella dei gestori di segnale.

Quando il processo figlio viene creato con clone, esegue la funzione application fn (arg). (Differisce dal fork, dove l'esecuzione continua nel child dal punto della chiamata fork originale.) L'argomento fn è un puntatore a una funzione chiamata dal processo child all'inizio della sua esecuzione. L'argomento arg viene passato alla funzione fn.

Quando ritorna l'applicazione della funzione fn (arg), il processo figlio termina. Il numero intero restituito da fn è il codice di uscita per il processo figlio. Il processo figlio può anche terminare esplicitamente chiamando exit (2) o dopo aver ricevuto un segnale fatale.

Modulo di informazione ottenuta:

Grazie per aver dedicato del tempo a leggere questo! :)

184
user476033
  • vfork() è un'ottimizzazione obsoleta. Prima di una buona gestione della memoria, fork() faceva una copia completa della memoria del genitore, quindi era piuttosto costosa. poiché in molti casi un fork() è stato seguito da exec(), che scarta la mappa di memoria corrente e ne crea una nuova, è stata una spesa inutile. Oggi fork() non copia la memoria; è semplicemente impostato come "copia su scrittura", quindi fork() + exec() è efficace quanto vfork() + exec().

  • clone() è la syscall utilizzata da fork(). con alcuni parametri, crea un nuovo processo, con altri crea un thread. la differenza tra loro è solo quali strutture di dati (spazio di memoria, stato del processore, stack, PID, file aperti, ecc.) sono condivise o meno.

145
Javier
  • execve() sostituisce l'immagine eseguibile corrente con un'altra caricata da un file eseguibile.
  • fork() crea un processo figlio.
  • vfork() è una versione storica ottimizzata di fork(), pensata per essere utilizzata quando execve() viene chiamato direttamente dopo fork(). Si è scoperto che funziona bene nei sistemi non MMU (dove fork() non può funzionare in modo efficiente) e quando fork()ing si elabora con un ingombro di memoria enorme per eseguire alcuni piccoli programmi (si pensi a Runtime.exec() di Java). POSIX ha standardizzato posix_spawn() per sostituire questi ultimi due usi più moderni di vfork().
  • posix_spawn() fa l'equivalente di fork()/execve() e consente anche un po 'di giocoleria fd in mezzo. Dovrebbe sostituire fork()/execve(), principalmente per piattaforme non MMU.
  • pthread_create() crea un nuovo thread.
  • clone() è una chiamata specifica per Linux, che può essere utilizzata per implementare qualsiasi cosa da fork() a pthread_create(). Dà molto controllo. Ispirato a rfork().
  • rfork() è una chiamata specifica Plan-9. Dovrebbe essere una chiamata generica, che consente diversi gradi di condivisione, tra processi e thread completi.
73
ninjalj
  1. fork() - crea un nuovo processo figlio, che è una copia completa del processo padre. I processi figlio e padre utilizzano spazi di indirizzi virtuali diversi, inizialmente popolati dalle stesse pagine di memoria. Quindi, man mano che vengono eseguiti entrambi i processi, gli spazi degli indirizzi virtuali iniziano a differire sempre di più, poiché il sistema operativo esegue una copia lenta delle pagine di memoria che vengono scritte da uno di questi due processi e assegna una copia indipendente delle pagine modificate di memoria per ogni processo. Questa tecnica si chiama Copy-On-Write (COW).
  2. vfork() - crea un nuovo processo figlio, che è una copia "rapida" del processo genitore. A differenza della chiamata di sistema fork(), i processi figlio e padre condividono lo stesso spazio di indirizzi virtuali. NOTA! Utilizzando lo stesso spazio di indirizzi virtuale, sia il genitore che il figlio usano lo stesso stack, il puntatore dello stack e il puntatore dell'istruzione, come nel caso del classico fork()! Per evitare interferenze indesiderate tra padre e figlio, che utilizzano lo stesso stack, l'esecuzione del processo parent viene bloccata fino a quando il bambino chiamerà exec() (crea un nuovo spazio di indirizzi virtuale e passa a uno stack diverso) o _exit() (terminazione del esecuzione del processo). vfork() è l'ottimizzazione di fork() per il modello "fork-and-exec". Può essere eseguito 4-5 volte più velocemente di fork(), perché a differenza di fork() (anche con COW nella mente), l'implementazione della chiamata di sistema vfork() non include la creazione di un nuovo spazio di indirizzi (allocazione e impostazione di nuovi directory di pagina).
  3. clone() - crea un nuovo processo figlio. Vari parametri di questa chiamata di sistema, specificano quali parti del processo padre devono essere copiate nel processo figlio e quali parti saranno condivise tra loro. Di conseguenza, questa chiamata di sistema può essere utilizzata per creare tutti i tipi di entità di esecuzione, a partire dai thread e finendo con processi completamente indipendenti. In effetti, la chiamata di sistema clone() è la base utilizzata per l'implementazione di pthread_create() e di tutta la famiglia delle chiamate di sistema fork().
  4. exec() - reimposta tutta la memoria del processo, carica e analizza il file binario eseguibile specificato, imposta il nuovo stack e passa il controllo al punto di ingresso dell'eseguibile caricato. Questa chiamata di sistema non restituisce mai il controllo al chiamante e serve per caricare un nuovo programma nel processo già esistente. Questa chiamata di sistema con fork() chiama insieme un modello classico di gestione dei processi UNIX chiamato "fork-and-exec".
39
ZarathustrA

Fork (), vfork () e clone () chiamano tutti do_fork () per fare il vero lavoro, ma con parametri diversi.

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

Per fork, il figlio e il padre hanno la tabella di pagine VM indipendente, ma poiché l'efficienza, fork non copierà davvero alcuna pagina, imposta semplicemente tutte le pagine scrivibili in sola lettura per il processo figlio. Quindi quando il processo figlio vuole scrivere qualcosa su quella pagina, si verifica un'eccezione di pagina e il kernel assegnerà una nuova pagina clonata dalla vecchia pagina con permesso di scrittura. Si chiama "copia in scrittura".

Per vfork, la memoria virtuale è esattamente di figlio e padre - solo per questo, padre e figlio non possono essere svegli contemporaneamente poiché si influenzeranno a vicenda. Quindi il padre dormirà alla fine di "do_fork ()" e si sveglierà quando il bambino chiamerà exit () o execve () da allora possiederà una nuova tabella di pagine. Ecco il codice (in do_fork ()) che il padre dorme.

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

Ecco il codice (in mm_release () chiamato da exit () ed execve ()) che sveglia il padre.

up(tsk->p_opptr->vfork_sem);

Per sys_clone (), è più flessibile poiché puoi inserire qualsiasi clone_flags. Quindi pthread_create () chiama questa chiamata di sistema con molti clone_flags:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

Riepilogo: fork (), vfork () e clone () creeranno processi figlio con differenti mount di condivisione delle risorse con il processo padre. Possiamo anche dire che vfork () e clone () possono creare thread (in realtà sono processi poiché hanno task_struct indipendente) poiché condividono la tabella di pagine VM con il processo padre.

6
user991800

Differenze tra fork () e vfork () Differenza tra fork-vfork the difference between fork and vfork

Il comportamento di Vfork () è stato spiegato in maggior dettaglio nel programma seguente.

[email protected] ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int n =10;
    pid_t pid = vfork(); //creating the child process
    if (pid == 0)          //if this is a chile process
    {
        printf("Child process started\n");
    }
    else//parent process execution
    {
        printf("Now i am coming back to parent process\n");
    }
    printf("value of n: %d \n",n); //sample printing to check "n" value
    return 0;
}
[email protected] ~}$ cc vfork_advanced.c
[email protected] ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted

Nota: di nuovo se si osserva l'esito di vfork non è definito. Il valore di "n" è stato stampato per la prima volta come 10, il che è previsto. Ma la prossima volta nel processo genitore ha stampato un valore di immondizia.