Newsgroup: it.comp.os.linux.development
Titolo: Ma perche' la fork() rompe i blocchi?

Piu' o meno  e' noto che la caratteristica peculiare della fork() e' la
seguente: non e' possibile stabilire se il processo padre verra'
eseguito prima del processo figlio.
Benissimo. Quello che mi pare pero' e' che non solo non si sa chi venga
eseguito prima, ma che il padre ed il figlio possano essere eseguiti "un
pezzo l'uno ed un pezzo l'altro"
Per esempio prendiamo questo programmino tirato giu' da un libro ("Linux
programming" della QUE) il cui sorgente sta tra i due tratteggi:
----------------------------------------------------------------------------------------------
/*
 * child.c - Simple fork usage
 */
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    pid_t child;
    if((child = fork()) == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if(child == 0) {
        puts("in child");
        printf("\tchild pid = %d\n", getpid());
        printf("\tchild ppid = %d\n", getppid());
        exit(EXIT_SUCCESS);
    } else {
        puts("in parent");
        printf("\tparent pid = %d\n", getpid());
        printf("\tparent ppid = %d\n", getppid());
    }
    exit(EXIT_SUCCESS);
}
-------------------------------------------------------------------------------------------------
ora il mio output e':
# ./child 
in parent
        parent pid = 1629
        parent ppid = 1412
in child
        child pid = 1630
        child ppid = 1
che a me andrebbe anche bene, ma l'output che viene riportato
dall'autore del libro (ovviamente i numeri sono diversi) e' il seguente:
# ./child 
in parent
        child pid = 1150
        child ppid = 1149
        parent pid = 1149
        parent ppid = 706
dacche' sembra che prima venga eseguito una parte del processo padre (e'
la stringa "in parent") poi viene eseguito il porcesso figlio per
intero, e poi di nuovo il processo padre. E NON e' un errore di stampa,
l'autore fa proprio notare che la prima riga viene dal padre, le
seguenti 3 dal figlio, le rimanenti 2 ancora dal padre.  
Ma perche? Io mi aspetterei che, d'accordo che non si sa chi venga
prima, ma che quando un processo parte vegna prima terminato quel
processo e poi venga fatto partire l'altro, o detto in altra maniera
estremamente piu' grezza: che i blocchi di codice tra parentesi graffe,
quando compilati e passati in ram, rimangano "tutti attaccati" per cui
le parti del programma tra le graffe vengano eseguiti per l'appunto "in
blocco".
Qui invece sembrerebbe di no e la cosa mi perplime. Qualcuno sa darmi
una spiegazione?
Spero di essermi spiegato, scusate il linguaggio poco tecnico,
grazie per l'attenzione,
b.

 una spiegazione?
E` che funziona cosi`. Tutto quello che hai descritto e` assolutamente 
normale per un sistema unix.
Il sistema e` multitasking e lo scheduler viene fatto girare ogni volta che 
passi il controllo al kernel (cioe` ogni giro di interrupt, e ad ogni 
system call).
Il solo codice che puoi aspettarti non venga interrotto da un altro 
processo e` quando fai una sistema call, quelle sono atomiche, tutto il 
resto non lo e`. Per questo ci sono i meccanismi di intercomunicazione fa 
processi.
Ciao
Simone

 b.
Spero di esserti stato d'aiuto.
Fabio Lopiano
-- 
Posted from 
flopiano.mm.eutelsat.org
 [194.214.173.28] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG


Mi sei stato utilissimo ed hai dato una spiegazione esauriente e molto chiara.
Studiare la programmazione di sistema in questa maniera e' veramente piacevole.
Grazie mille a te ed anche a Simone Piccardi,
b.

 consente di fare cose molto interessanti.
Vado a memoria, ma credo ci sia qualcosa anche sotto win32. Non fosse che 
NT si dice POSIX-compatibile

impossibile simularla in ambiente Windows)
Con calma :) Questa è un po' grossa, se permetti :) Qualsiasi sistema 
operativo che abbia l'attributo copy-on-write per le pagine di memoria 
(ma questo in fondo nemmeno serve), e che mantenga una tabella dei file 
descriptor, può implementare fork
Il problema in Windows è che i processi sono basati su un'architettura 
client-server, e fork() fa saltare tutta la parte di inizializzazione 
delle DLL, tra cui ad esempio il codice in kernel32.dll che connette il 
processo al sottosistema di runtime. Questo è fonte di un sacco di guai 
:) ad esempio scrivere su una console testuale diventa addirittura 
pericoloso (le console sono gestite dal processo server). Anche user32 e 
gdi32 mantengono variabili flag globali per sapere se il processo è 
stato già inizializzato (anche la parte grafica è basata su un modello 
vagamente client-server), e se tali flag mantengono il valore che 
avevano nel parent, beh, il child verrà fuori un pochino menomato. 
Diciamo che è un sistema poco "fork friendly". Diciamo che un processo 
Win32 è meglio che non forki mai (nessuno vieta di forkare processi 
POSIX, comunque, che è quello a cui sto lavorando)
<mumble mumble...>
Un'idea sarebbe per ogni fork camminarsi tutta la lista delle DLL e 
chiamare di ognuna l'entry point specificando che è un nuovo processo ad 
essere stato creato, per reinizializzarle daccapo... anche questo 
potrebbe essere fonte di guai, ma è sempre un'idea...
</mumble mumble...>
-- 
Nato nel vizio, morto nel supplizio
Nato nel peccato, ad entrare sia invitato

eseguiti per l'appunto "in blocco".
Una riga di codice (specialmente qualcosa come printf) in realtà può 
corrispondere a numerosissime chiamate a procedura. Il sistema operativo 
è liberissimo di interrompere la chiamata in qualsiasi momento e passare 
il controllo ad un altro thread. Inoltre, printf è una funzione di I/O, 
e le funzioni di I/O hanno effetti non banali ed imprevedibili sulla 
schedulazione dei thread: in pratica, già non c'è nessun modo per 
stabilire l'ordine di esecuzione di due thread, per di più printf 
incasina ulteriormente le cose, mettendo irrimediabilmente in pausa un 
thread finchè l'operazione di scrittura non è completata. Se proprio 
vuoi stabilire l'ordine di esecuzione di due thread (ad esempio fermare 
il parent finchè il child non ha raggiunto un certo punto), devi 
ricorrere ai meccanismi di sincronizzazione. Vorrei farti un esempio, ma 
al momento conosco solo l'API pthread, che non garantisce di funzionare 
tra più processi


 stabilire l'ordine di esecuzione di due thread, per di più printf
Vabbe', allora a questo punto faro' la domanda piu' vecchia e stupida del
mondo:
qual e' la differenza tra un processo ed un thread?
Ho gia' trovato la definizione anche in riviste piu' o meno specialistiche,
ma tutti parlano per parabole, per metafore, per ellissi...
Posso dire con certezza che se il sistema operativo comincia ad eseguire un
thread allora  non lo interrompera' fino alla fine? Posso dire che il
thread, per intenderci, e' l' "atomo" della schedulazione al di sotto del
quale non si schedula nulla?
Ed in C il thread a cosa corrisponde? Ad un instruzione che finisce col ";"
o se no a che cosa?
Scusate so che e' una domanda banale, ma in questo ng qualche anno fa mi
pare d'aver letto che i thread sono gruppi di processi  ("che condividono lo
stesso spazio di indirizzamento del processo padre" lo ricorodo a memoria
anche se non l'ho mai capito molto), mentre sono pure certo d'aver letto da
qualche altra parte (ma probabilmente mi sbaglio) che il thread e' come
dire, un "pezzo" di un processo e non un insieme di processi.
Grazie per l'attenzione,
b.

un processo e non un insieme di processi. 
è vero anche questo. Tu non farci caso, sono sottigliezze puramente 
accademiche. Nel mondo reale nessuno chiama i thread "processi". Nemmeno 
lo standard Unix dice una cosa del genere. I processi sono una cosa, i 
thread un'altra. Li chiama Tanenbaum "processi", ma Tanenbaum è un 
professore universitario :)



Grazie mille per la dettagliata e chiara risposta che mi salvo una volta per
tutte.
Stammi bene,
b.

 writes:
AL> printf č una funzione di I/O,
Qualcosa  di  leggermente  piu`  "fetido" per  la  prevedibilta`:  I/O
bufferizzato per  cui la chiamata a  write() puo` o  non puo` avvenire
(dipende dal fatto che il buffer venga riempito o no).
(Folklore: ho visto un mio amico  fare la tesi su un sistema real time
dove  printf()  non era  rientrante  e  scontrarsi  con uno  dei  piu`
carognosi bug quantistici.)
-- 
Gian  | main(){printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
Uberto|                        David Korn, 1987 ioccc best one liner 
Lauri |-------------------------------------------------------------------
(aka saint) >>Vedere X-Nospam nell'header - See X-Nospam field in Header<<