Wizualnie, co dzieje się widelec () w pętli


Próbowałem zrozumieć zachowanie
fork ()
. Tym razem w
for-loop
. Przestrzegaj następującego kodu:
#include <stdio.h>void main()
{
int i; for (i=0;i<3;i++)
{
fork();// This printf statement is for debugging purposes
// getppid(): gets the parent process-id
// getpid(): get child process-id printf("[%d] [%d] i=%d\n", getppid(), getpid(), i);
} printf("[%d] [%d] hi\n", getppid(), getpid());
}
Oto wynik

:
[6909][6936] i=0
[6909][6936] i=1
[6936][6938] i=1
[6909][6936] i=2
[6909][6936] hi
[6936][6938] i=2
[6936][6938] hi
[6938][6940] i=2
[6938][6940] hi
[1][6937] i=0
[1][6939] i=2
[1][6939] hi
[1][6937] i=1
[6937][6941] i=1
[1][6937] i=2
[1][6937] hi
[6937][6941] i=2
[6937][6941] hi
[6937][6942] i=2
[6937][6942] hi
[1][6943] i=2
[1][6943] hi

Jestem osobą bardzo wizualną i dlatego jedynym sposobem, aby naprawdę zrozumieć rzeczy, jest zrobienie diagramu. Mój instruktor powiedział, że to będzie 8

Witamy

sprawozdania. Napisałem i uruchomiłem kod i rzeczywiście było 8 instrukcji

hi
... Ale naprawdę tego nie rozumiałem. Więc narysowałem następujący diagram:

Wykres zaktualizowany o komentarze :)


Obserwacje:
>
  • Proces macierzysty (główny) musi powtórzyć cykl 3 razy. Następnie wywoływana jest funkcja printf
  • W każdej iteracji rodzica pętli for wywoływana jest funkcja fork ()
  • Po każdym wywołaniu fork (), i jest inkrementowane, więc każde dziecko rozpoczyna pętlę for w punkcie i, zanim zostanie zwiększone
  • Na końcu każdej pętli for wypisywane jest „hi”


Oto moje pytania:
>
  • Czy mój schemat jest prawidłowy
  • Dlaczego dane wyjściowe obejmują dwa wystąpienie
    i = 0
  • Jaka wartość
    i
    jest przenoszona na każde dziecko po fork () Jeśli ta sama wartość
    i
    zostanie przeniesiona, kiedy „rozwidlanie” się zatrzyma
  • Czy zawsze jest tak, że
    2 ^ n - 1
    byłby sposobem zliczenia liczby podzielonych dzieci Więc tutaj
    n = 3
    , co oznacza
    2 ^ 3 - 1 = 8 - 1 = 7
    dzieci, które jest poprawne

Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto jak to rozgryźć, zaczynając od pętli
for
.
  • Pętla zaczyna się w pozycji Parent,
    i == 0
  • Nadrzędny
    fork ()
    c, tworząc element potomny 1.
  • Masz teraz dwa procesy. Oba drukują
    i = 0
    .
  • Pętla jest restartowana w obu procesach, teraz
    i == 1
    .
  • Rodzic i dziecko 1
    fork ()
    , tworząc elementy potomne 2 i 3.
  • Masz teraz cztery procesy. Wszystkie cztery drukują
    i = 1
    .
  • Pętla jest ponownie uruchamiana we wszystkich czterech procesach, teraz
    i == 2
    .
  • Rodzic i dzieci od 1 do 3 wszystkie
    fork ()
    , tworząc elementy podrzędne od 4 do 7.
  • Masz teraz osiem procesów. Wszystkie osiem drukuje
    i = 2
    .
  • Pętla jest ponownie uruchamiana we wszystkich ośmiu procesach, teraz
    i == 3
    .
  • Pętla kończy się we wszystkich ośmiu procesach, ponieważ
    i & < 3
    nie jest już prawdą.
  • Wszystkie osiem procesów drukuje
    hi
    .
  • Wszystkie osiem procesów zostało zakończonych.

Zatem
0
drukowane jest dwukrotnie,
1
drukowane cztery razy,
2
drukowane 8 razy i drukowane
hi
. 8 czasy.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

  • Tak to prawda. (patrz poniżej)
  • Nie,
    i ++
    jest uruchomiony po wzywam
    fork
    , ponieważ tak działa pętla
    for
    .
  • Jeśli wszystko pójdzie dobrze, to tak. Pamiętaj jednak, że
    fork
    może się nie powieść.

Małe wyjaśnienie do drugiego:
for (i = 0;i < 3; i++)
{
fork();
}

wygląda jak:
i = 0;
while (i < 3)
{
fork();
i++;
}

Zatem
i
w procesach rozwidlonych (zarówno nadrzędnych, jak i podrzędnych) jest wartością przed przyrostem. Jednak przyrost jest wykonywany zaraz po
fork ()
, więc moim zdaniem diagram można uznać za poprawny.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Aby odpowiedzieć na pytania jeden po drugim:

Czy mój schemat jest prawidłowy?

Tak, zasadniczo. To także bardzo dobry schemat.
Oznacza to, że poprawne jest interpretowanie etykiet
i = 0
i tak dalej jako odnoszących się do iteracji pełnej pętli. Jednak diagram

nie

pokazuje, że po każdej
fork ()
część bieżącej iteracji pętli po wywołaniu
fork ()
jest również wykonywana przez proces rozwidlony podrzędny.

Dlaczego w wyniku występują dwa wystąpienia
i = 0
?

Ponieważ masz
printf ()
po
fork ()
, więc jest ono wykonywane zarówno przez proces nadrzędny, jak i właśnie rozwidlony proces potomny. Jeśli przesuniesz
printf ()
przed
fork ()
, zostanie on wykonany tylko przez rodzica (ponieważ proces potomny jeszcze nie istnieje).

Jaka wartość
i
jest przenoszona na każde dziecko po
fork ()
? Jeśli ta sama wartość
i
zostanie przeniesiona, kiedy „rozwidlanie” się zatrzyma?

Wartość
i
nie zmienia się na
fork ()
, więc dziecko widzi tę samą wartość co jego Parent.
W przypadku
fork ()
należy pamiętać, że jest wywoływana raz, ale zwracana dwukrotnie - raz w procesie nadrzędnym i raz w nowo sklonowanym procesie potomnym.
Aby uzyskać prostszy przykład, rozważ następujący kod:
printf("This will be printed once.\n");
fork();
printf("This will be printed twice.\n");
fork();
printf("This will be printed four times.\n");
fork();
printf("This will be printed eight times.\n");

Proces potomny utworzony przez
fork ()
jest (prawie) dokładnym klonem swojego rodzica i dlatego z własnego punktu widzenia "pamięta" jest jego rodzicem, dziedzicząc cały stan rodzica proces (w tym wszystkie wartości zmiennych, stos wywołań i instrukcje wykonywalne). Jedyną bezpośrednią różnicą (inną niż metadane systemowe, takie jak identyfikator procesu zwracany przez
getpid ()
) jest wartość zwracana przez
fork ()
, która w elemencie podrzędnym będzie wynosić zero proces, ale niezerowy (w rzeczywistości identyfikator procesu potomnego) w obiekcie nadrzędnym.

Czy zawsze jest tak, że
2 ^ n - 1
byłby sposobem zliczenia liczby podzielonych dzieci? Więc tutaj
n = 3
, co oznacza
2 ^ 3 - 1 = 8 - 1 = 7
dzieci, które jest poprawne?

Każdy proces, który
fork ()
staje się dwoma procesami (z wyjątkiem nietypowych błędów, w których
fork ()
może się nie powieść). Jeśli rodzic i dziecko nadal wykonują ten sam kod (to znaczy nie sprawdzają wartości zwracanej funkcji
fork ()
ani własnego identyfikatora procesu i rozgałęziają się do różnych ścieżek kodu na jego podstawie), to każde następne rozwidlenie podwaja liczbę procesów. Zatem tak, po trzech widełkach otrzymujesz łącznie 2³ = 8 procesów.

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się