2014-10-23 7 views
6

Questa è una sorta di una questione tecnica, forse mi può aiutare se sai di C e UNIX (o forse è una domanda davvero newbie!)A proposito di puntatori dopo fork()

Una domanda si avvicinò oggi, mentre analizzando alcuni codici nel nostro corso sui sistemi operativi. Stiamo imparando cosa significa "forgiare" un processo in UNIX, sappiamo già che crea una copia del processo corrente parallelamente ad esso e hanno sezioni separate di dati.

Ma poi ho pensato che forse, se si crea una variabile e un puntatore che punta su di esso prima di fare fork(), perché il puntatore memorizza l'indirizzo di memoria della variabile, si potrebbe provare a modificare il valore di tale variabile da il processo figlio usando quel puntatore.

abbiamo provato un codice simile a questo in classe:

#include <stdio.h> 
#include <sys/types.h> 
#include <stdlib.h> 

int main(){ 
    int value = 0; 
    int * pointer = &value; 
    int status; 

    pid_t pid; 

    printf("Parent: Initial value is %d\n",value); 

    pid = fork(); 

    switch(pid){ 
    case -1: //Error (maybe?) 
     printf("Fork error, WTF?\n"); 
     exit(-1); 

    case 0: //Child process 
     printf("\tChild: I'll try to change the value\n\tChild: The pointer value is %p\n",pointer); 
     (*pointer) = 1; 
     printf("\tChild: I've set the value to %d\n",(*pointer)); 

     exit(EXIT_SUCCESS); 
     break; 
    } 

    while(pid != wait(&status)); //Wait for the child process 

    printf("Parent: the pointer value is %p\nParent: The value is %d\n",pointer,value); 

    return 0; 
} 

Se lo si esegue, si otterrà qualcosa di simile:

principale: il valore iniziale è 0

Bambino: proverò a modificare il valore

Bambino: il valore del puntatore è 0x7fff733b0c6c

bambini: ho impostato il valore su 1

principale: il valore del puntatore è 0x7fff733b0c6c Parent

: Il valore è 0

E 'ovvio che il processo figlio non ha influenzato in tutto il processo genitore. Francamente, mi aspettavo qualche errore di "segmentation fault", a causa dell'accesso a un indirizzo di memoria non consentito. Ma cosa è successo veramente?

Ricorda, non sto cercando un modo per comunicare i processi, non è questo il punto. Quello che voglio sapere è che cosa ha fatto il codice. All'interno del processo figlio, il cambiamento è visibile, quindi ha fatto qualcosa.

La mia ipotesi principale è che i puntatori non sono assoluti alla memoria, sono relativi allo stack del processo. Ma non sono stato in grado di trovare una risposta (nessuno in classe lo sapeva, e googlando ho appena trovato alcune domande sulla comunicazione di processo) quindi mi piacerebbe sapere da voi, si spera che qualcuno lo sappia.

Grazie per aver dedicato del tempo a leggere!

+0

è possibile inserire 'printf (" Parent: il valore del puntatore è% p \ nIl valore è% d \ n ", puntatore, valore);' nel caso predefinito di 'switch()' – Haris

+2

http://en.wikipedia.org/wiki/Virtual_memory – Mat

+0

"* Francamente, mi aspettavo qualche errore di" segmentation fault ", a causa dell'accesso a un indirizzo di memoria non consentito. *" Non ha senso. Pensaci. Il codice che il bambino esegue è perfettamente legale. L'unica domanda è - lo fa o non interessa il genitore? –

risposta

11

La chiave qui è il concetto di uno spazio di indirizzi virtuali.

I processori moderni (dicono qualcosa di più recente di un 80386) dispongono di un'unità di gestione della memoria che esegue il mapping da uno spazio di indirizzamento virtuale per processo a pagine di memoria fisica sotto il controllo del kernel.

Quando il kernel imposta un processo crea un insieme di voci della tabella di pagina per quel processo che definiscono le pagine di memoria fisica per il mapping dello spazio di indirizzi virtuale, ed è in questo spazio di indirizzi virtuali che il programma esegue.

Concettualmente, quando si esegue il fork, il kernel copia le pagine di processo esistenti in una nuova serie di pagine fisiche e imposta le nuove tabelle di pagine dei processi in modo che, per quanto riguarda il nuovo processo, sia in esecuzione nella stessa layout di memoria come quello originale, mentre in realtà si occupava di memoria fisica completamente diversa.

Il dettaglio è più sottile in quanto nessuno vuole perdere tempo a copiare centinaia di MB di dati, a meno che non sia necessario. Quando il processo chiama fork() il kernel imposta un secondo set di voci della tabella di pagina (per il nuovo processo), ma le punta alle stesse pagine fisiche del processo originale, quindi imposta il flag in entrambi i gruppi di pagine per fare in modo che gli mmu li considerino di sola lettura ...

Non appena un processo scrive su una pagina, l'unità di gestione della memoria genera un errore di pagina (a causa della voce PTE con il flag di sola lettura impostato), e il gestore degli errori di pagina quindi assegna una nuova pagina dalla memoria fisica, copia i dati, aggiorna la voce della tabella di pagine e imposta le pagine in lettura/scrittura. In questo modo, le pagine vengono effettivamente copiate solo la prima volta che un processo tenta di apportare una modifica a una copia nella pagina di scrittura, e la manina della mano passa inosservata da entrambi i processi.

Cordiali saluti, Dan.

3

Il bambino ha modificato un puntatore che è perfettamente legale nel suo spazio indirizzo perché è una copia del suo genitore. Non c'è stato alcun effetto sul genitore perché la memoria non è logicamente condivisa. Ogni processo prende la sua strada separata dopo la forcella.

UNIX ha un certo numero di modi per creare memoria condivisa (in cui un processo può modificare la memoria e avere quella modifica vista da un altro processo), ma fork non è uno di questi. Ed è una buona cosa perché altrimenti la sincronizzazione tra genitore e figlio sarebbe quasi impossibile.

+0

Ma perché è legale? Non è quell'indirizzo di memoria di proprietà del genitore? Il valore è lo stesso – javierbg

+1

Il bambino non sa o si cura di ciò che il genitore possiede. Sì, il genitore possiede quell'indirizzo di memoria (nel suo spazio indirizzo). Anche il bambino possiede quell'indirizzo di memoria (nel suo spazio indirizzo). Il genitore e il bambino iniziano come copie ma possono quindi andare in modi separati. Gli spazi di memoria sono autorizzati a divergere dopo la forcella, ma sono gli stessi fino ad esso. –

+0

Quindi, presumo che i puntatori non memorizzino gli indirizzi di memoria assoluti nella memoria fisica. È corretto? – javierbg

4

Logicamente, il processo fork() ottiene la propria copia indipendente di più o meno l'intero stato del processo padre. Non poteva funzionare se i riferimenti nel bambino si riferivano alla memoria appartenente al genitore.

I dettagli di come un particolare kernel simile a UNIX rende tale lavoro può variare. Linux implementa la memoria del processo figlio tramite pagine copy-on-write, il che rende fork() relativamente economico rispetto ad altre possibili implementazioni. In tal caso, i puntatori del bambino puntano davvero alla memoria del processo genitore, fino a quando il figlio o il genitore tenta di modificare la memoria, in quel momento viene creata una copia per il figlio da utilizzare. Tutto ciò si basa sul sistema di memoria virtuale sottostante. Altri sistemi UNIX e UNIX possono farlo e averlo fatto in modo diverso.

+0

Al momento non so molto della memoria virtuale, quindi non lo sapevo. Grazie! – javierbg