Programowanie współbieżne i rozproszone/PWR Ćwiczenia 2: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mengel (dyskusja | edycje)
mNie podano opisu zmian
Mengel (dyskusja | edycje)
mNie podano opisu zmian
Linia 78: Linia 78:
* ma te same uprawnienia, te same otwarte pliki itd. (tym zajmiemy się w kolejnym laboratorium)
* ma te same uprawnienia, te same otwarte pliki itd. (tym zajmiemy się w kolejnym laboratorium)


A oto przykład ilustrujący wykorzystanie funkcji <tt>fork()</tt> do tworzenia nowego procesu:
A oto przykład ilustrujący wykorzystanie funkcji <tt>fork()</tt> do tworzenia nowego procesu. Przykład ten możesz znaleźć w plikach przygotowanych do zajęć pod nazwą <tt>proc_fork.c</tt>.


  #include <stdio.h>
  #include <stdio.h>
Linia 86: Linia 86:
  #include <sys/wait.h>
  #include <sys/wait.h>
  #include "err.h"
  #include "err.h"
  int main ()
  int main ()
  {
  {
  pid_t pid;
'''1:'''  pid_t pid;
   /* wypisuje identyfikator procesu */
   /* wypisuje identyfikator procesu */
  printf("Moj PID = %d\n", getpid());
'''2:'''  printf("Moj PID = %d\n", getpid());
   /* tworzy nowy proces */
   /* tworzy nowy proces */
   '''switch''' (pid = fork()) {
   '''switch''' (pid = fork()) {
    '''case''' -1: /* blad */
'''3:'''    '''case''' -1: /* błąd */
       syserr("Error in fork\n");
       syserr("Error in fork\n");
    '''case''' 0: /* proces potomny */
'''4:'''    '''case''' 0: /* proces potomny */
       printf("Jestem procesem potomnym. Moj PID = %d\n", getpid());
       printf("Jestem procesem potomnym. Mój PID = %d\n", getpid());
       printf("Jestem procesem potomnym. Wartosc przekazana przez fork() = %d\n", pid);
       printf("Jestem procesem potomnym. Wartość przekazana przez fork() = %d\n", pid);
       '''return''' 0;
       '''return''' 0;
     '''default''': /* proces macierzysty */
     '''default''': /* proces macierzysty */
      printf("Jestem procesem macierzystym. Moj PID = %d\n", getpid());
'''5:'''      printf("Jestem procesem macierzystym. Mój PID = %d\n", getpid());
       printf("Jestem procesem macierzystym. Wartosc przekazana przez fork() = %d\n", pid);
       printf("Jestem procesem macierzystym. Wartość przekazana przez fork() = %d\n", pid);
       /* czeka na zakonczenie procesu potomnego */
       /* czeka na zakończenie procesu potomnego */
      '''if'''(wait(0) == -1)
'''6:'''      '''if'''(wait(0) == -1)
         syserr("Error in wait\n");
         syserr("Error in wait\n");
       '''return''' 0;
       '''return''' 0;
Linia 110: Linia 109:
  }
  }


Przeanalizujmy


<!--
<!--

Wersja z 09:07, 12 cze 2006

Tematyka laboratorium

  1. Przypomnienie zasad tworzenia plików Makefile
  2. Kompilacja programów w środowisku Unix
  3. Procesy w systemie Unix: tworzenie, kończenie,


Literatura uzupełniająca

  1. M. K. Johnson, E. W. Troan, Oprogramowanie użytkowe w systemie Linux, rozdz. 9.2.1 i 9.4.1-9.4.5
  2. W. R. Stevens, Programowanie zastowań sieciowych w systemie UNI, rozdz. 2.5.1-2.5.4
  3. M. J. Bach, Budowa systemu operacyjnego UNIX, rozdz. 7.1, 7.3-7.5
  4. man do poszczególnych funkcji systemowych

Scenariusz zajęć

Identyfikator procesu

Każdy proces w systemie ma jednoznaczny identyfikator nazywany potocznie PID (od angielskiego: Process ID). Identyfikatory aktualnie wykonujących się procesów możesz poznać wykonując w Linuksie polecenie ps.

Ćwiczenie
Wykonaj polecenie ps. Zobaczysz wszystkie uruchomione przez Ciebie procesy w tej sesji. Znajdzie się wsród nich proces ps i bash (lub inny stosowany przez Ciebie interpreter poleceń), który analizuje i wykonuje Twoje polecenia. Pierwsza kolumna to PID procesu, a ostatnia to polecenie, które dany proces wykonuje. Więcej informacji na temat polecenia ps uzyskasz wywołując man ps.

Z poziomu programisty, proces może poznać swój PID wywołując funkcję systemową:

pid_t getpid();

Wartości typu pid_t reprezentują PIDy procesów. Najczęściej jest to długa liczba całkowita (long int), ale w zależności od wariantu systemu operacyjnego definicja ta może być inna. Dlatego lepiej posługiwać się nazwą pid_t.

Tworzenie nowego procesu

W Linuksie, tak jak we wszystkich systemach uniksowych, istnieje hierarchia procesów. Każdy proces poza pierwszym procesem w systemie (procesem init o PIDzie 1) jest tworzony przez inny proces. Nowy proces nazywamy procesem potomnym, a proces który go stworzył nosi nazwę procesu macierzystego.

Do tworzenia procesów służy funkcja systemowa:

pid_t fork();

Powrót z wywołania tej funkcji następuje dwa razy:

  • w procesie macierzystym, w którym wartością przekazywaną przez funkcję fork jest PID nowo utworzonego potomka,
  • w procesie potomnym, w którym funkcja przekazuje w wyniku 0.

Jak dokładnie działa funkcja systemowa fork()? Proces w systemie Unix jest wygodnie wyobrażać sobie jako obiekt składający się z trzech części:

Proces Uniksowy. Podział na logiczne części.
Wykonywany kod Dane:
  • w szczególności wszystkie zmienne procesu
Dane systemowe:
  • PID,
  • PID ojca
  • otwarte pliki
  • itd

Funkcja systemowa fork tworzy nowy proces i kopiuje do niego wszystkie powyższe elementy, zmieniając jedynie te elementy, który muszą zostać zmienione (na przykład PID). Zatem nowy proces potomny:

  • wykonuje taki sam kod jak proces macierzysty;
  • dziedziczy po procesie macierzystym całą historię wykonania, bo stos wykonania jest także kopiowany; oznacza to w szczególności, że

wykonanie w procesie potomnym zaczyna się od następnej instrukcji po fork().

  • ma te same zmienne co proces macierzysty i do tego zmienne te mają te same wartości co w procesie macierzystym. Jednak przestrzenie adresowe tych procesów są rozłączne: każdy ma swoją kopię zmiennych. Oznacza to m.in. to, że zmiana wartości zmiennej w procesie potomnym nie jest odzwierciedlana w procesie macierzystym i na odwrót.
  • ma te same uprawnienia, te same otwarte pliki itd. (tym zajmiemy się w kolejnym laboratorium)

A oto przykład ilustrujący wykorzystanie funkcji fork() do tworzenia nowego procesu. Przykład ten możesz znaleźć w plikach przygotowanych do zajęć pod nazwą proc_fork.c.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "err.h"
int main ()
{

1: pid_t pid;

  /* wypisuje identyfikator procesu */

2: printf("Moj PID = %d\n", getpid());

  /* tworzy nowy proces */
  switch (pid = fork()) {

3: case -1: /* błąd */

      syserr("Error in fork\n");

4: case 0: /* proces potomny */

      printf("Jestem procesem potomnym. Mój PID = %d\n", getpid());
      printf("Jestem procesem potomnym. Wartość przekazana przez fork() = %d\n", pid);
      return 0;
    default: /* proces macierzysty */

5: printf("Jestem procesem macierzystym. Mój PID = %d\n", getpid());

      printf("Jestem procesem macierzystym. Wartość przekazana przez fork() = %d\n", pid);
      /* czeka na zakończenie procesu potomnego */

6: if(wait(0) == -1)

        syserr("Error in wait\n");
      return 0;
 } /*switch*/
}

Przeanalizujmy