[LinuxFocus-icon]
Strona G��wna  |  Mapa Serwisu  |  Indeks  |  Szukaj

Nowo�ci | Archiwum | Linki | O Nas
Ten dokument jest dost�pny w nast�puj�cych j�zykach: English  Castellano  ChineseGB  Deutsch  Francais  Italiano  Nederlands  Russian  Turkce  Polish  

[Leonardo]
Leonardo Giordani
<leo.giordani(at)libero.it>

O Autorze:

Student Telecomunikacji na Politechnice w Mediolanie, pracuje jako administrator sieciowy, interesuje si� programowaniem. (przewa�nie Assembler i C/C++). Od 1999 pracuje tylko na Linux/Unix.

Translated to English by:
Leonardo Giordani <leo.giordani(at)libero.it>

Zawarto��:


 

Programowanie wsp�bie�ne - wprowadzenie i zasady.

[run in paralell]

Notka:

Ta seria artyk��w wprowadza czytelnika w poj�cia multitaskingu w systemie Linux. Rozpoczynaj�c od teoretycznego wprowadzenia do multitaskingu i ko�cz�c na napisaniu pe�nej aplikacji ukazuj�cej w pe�ni komunikacj� pomi�dzy procesami z prost�, ale wydajn� komunikacj� protoko��w. Potrzebn� wiedz� do zrozumienia artyku�u jest :

Wszystkie wyja�nienia mo�na znale�� w manualach kt�rych umiejscowienie jest pokazane w nawiasach. Wszystkie biblioteki glibc s� udokumentowane na stronach informacji gnu (info Libc, albo info:/libc/Top w konquerorze).
_________________ _________________ _________________

 

Wprowadzenie

Punktem zwrotnym w historii system�w operacyjnych by�o wprowadzenie poj�cia multiprogramingu(programowanie wielow�tkowe), czyli techniki, kt�ra daje mo�liwo�� wykonywania wielu program�w w odpowiednim porz�dku by uzyska� wi�ksz� stabilno�� zasob�w systemu. Wezmy pod uwag� na przyk�ad prost� stacje robocz�, na kt�rej u�ytkownik mo�e korzysta� z procesora tekstu, odtwarzacza audio, drukarki, przegl�darki internetowej i wielu innych. Jak wiemy to jest tylko ma�a cz�� d�ugiej listy program�w, kt�re mo�emy w��czy� na naszym komputerze.  

Poj�cie procesu

Gdy programy s� uruchomione w jednym czasie to w�wczas wyst�puj� komplikacje w systemie operacyjnym. W celu unikni�cia konflikt�w pomi�dzy uruchomionymi programami nale�y kapsu�kowa� ka�dy z nich z informacjami potrzebnymi do ich wykonania.

Przed tym jak poznamy co si� dok�adnie dzieje w Linuksie, opanujmy troch� technicznej nomenklatury: mamy URUCHOMIONY PROGRAM, czyli zbi�r instrukcji KOD PROGRAMU kt�re s� wykonywane w danym czasie, PRZESTRZE� PAMI�CI czyli cz�� pami�ci, kt�ra zosta�a zarezerwowana przez dane i STATUS PROCESORA kt�ry jest warto�ci� parametru procesora, tak jak flaga albo licznik programu (adres instrukcji, kt�ra zostanie wykonana w nast�pnej kolejno�ci).

Zdefiniowali�my poj�cie URUCHOMIONEGO PROGRAMU jako obiektu posiadaj�cego KOD, PRZESTRZE� PAMI�CI i STATUS PROCESORA. Je�li w pewnym czasie podczas wykonywania programu zapami�tamy te informacje i zamienimy je z tymi samym zestawem informacji, kt�ry wezmiemy z innego uruchomionego programu i robi�c tak dalej, b�dziemy przeplata� te czynno�ci tak opisane czynno�ci mo�emy nazwa� PROCESEM (albo ZADANIEM), czyli poj�cie PROCES jest u�ywane do opisu uruchomionego programu.

Wyt�umaczmy co si� dzia�o z nasz� stacj� robocz�, m�wili�my o tym wcze�niej : w ka�dym momencie jest wykonywane tylko jedno zadanie (mikroprocesor jest w stanie obs�u�y� tylko jedno zadanie) i maszyna wykonuje cz�� kodu programu, nast�pnie po jakim� czasie (czas ten nazywamy kwantem ang. QUANTUM) nasz proces zostaje zawieszony i informacje dotycz�ce jego s� zapisane i zamienione z informacjami dotycz�cymi innego czekaj�cego na wykonanie procesu. Ten kod zostanie wykonany ponownie po up�yni�ciu kwantu czasu i tak dalej. I ca�a ta procedura jest nazywana multitaskingiem lub wielozadaniowo�ci�.

Sam proces multitaskingu ukazuje nam wiele problem�w i wi�kszo�� z nich nie jest trywialna wiezmy pod uwag� proces kolejkowania oczekuj�cych proces�w (tzw. SCHEDULING, czyli harmonogram), problemy te s� rozwi�zywane inaczej w ka�dym systemie (uzale�nione jest to od architektury systemu operacyjnego). Mo�liwe, �e to b�dzie tematem przysz�ego artyku�u, kt�ry wprowadzi was w tematyk� kodu Kernela.  

Procesy w Linuksie i Uniksie

Powiedzmy co� o procesach uruchamianych na naszej maszynie. Komend� daj�c� nam troch� informacji jest ps(1), kt�ra jest skr�tem od (process status) status procesu. Uruchamiaj�c w shelu nasz� komend� na ekranie zostanie wy�wietlone co� w rodzaju :

  PID TTY          TIME CMD
 2241 ttyp4    00:00:00 bash
 2346 ttyp4    00:00:00 ps

Ta lista nie jest kompletna, ale skoncentrujmy si� na tym co mamy : ps daje nam list� proces�w, kt�re s� uruchomione na naszym terminalu. W ostatniej kolumnie jest wykazane jak wygl�da nazwa procesu, kt�ry zosta� uruchomiony (na przyk�ad "mozila" dla Mozilla Web Browser (przegl�darka internetowa) albo "gcc" dla GNU Compiler Collection)). Niestety ps jest na li�cie proces�w poniewa� lista ta pokazuje procesy ju� uruchomione. Innym wypisanym procesem jest Bourne Again Shell, shell kt�ry jest uruchomiony na moim terminalu.

Zostawmy na chwile informacje jakie przekazuje nam TIME i TTY i spoj�my na PID Process IDentifier. Numer pid jest unikalnym dodatnim numerem (r�nym od zera), kt�ry jest przypisany do ka�dego uruchomionego procesu, gdy proces zostanie skasowany, to pid nie mo�e by� u�yty ponownie z innym procesem, ale gdy proces trwa to pid zawsze jest przyporz�dkowany do tego samego procesu. Wszystkie te wiadomo�ci jakie uzyskali�my dzi�ki poleceniu ps, b�d� prawdopodobnie inne gdy uruchomimy to polecenie ponownie. Mo�na to przetestowa� uruchamiaj�c innego shella, bez zamykania tego w kt�rym jeste�my teraz i uruchommy ps, zostanie nam wypisana ta sama lista proces�w ale z innymi numerami pid, ten test pokazuje man �e chocia� programy s� te same, to nie s� to ju� te same procesy.

Mo�emy tak�e uzyska� list� uruchomionych proces�w w inny spos�b, manual do ps m�wi, �e gdy uruchomimy ps z argumentem -e kt�ry znaczy "wybierz wszystkie procesy", to zostanie nam pokazana d�uga lista proces�w przedstawion� jak powy�ej. Najlepiej jest przekierowa� nasz� list� do pliku np. ps.log :

ps -e > ps.log

Teraz mo�emy czyta� i edytowa� nasz plik w edytorze kt�ry preferujemy (albo pro�ciej komend� less), jak zacz�li�my na pocz�tku artyku�u nasze numery pid by�y wi�ksze ni� si� spodziewali�my. Teraz wypisali�my list� nie tylko proces�w uruchomionych przez nas (bezpo�rednio z wiersza polece�) ale tak�e wiele innych, wiele z nich ma dziwne nazwy : numery i ich to�samo�� zale�� od konfiguracji twojego systemu, ale zawsze s� jakie� wsp�lne rzeczy. Po pierwsze, nie wa�ne jak� konfiguracje systemu masz to zawsze procesem kt�rego pid jest r�wny 1 to proces "init", ojciec wszystkich proces�w. Dla niego jest przyporz�dkowany jest numer 1 dlatego, bo on zawsze jest uruchamiany jako pierwszy przez system operacyjny. Nast�pn� rzecz�, kt�r� mo�emy z �atwo�ci� zauwa�y�, jest to, �e nazwy wielu proces�w ko�cz� si� na "d", nazywane one s� demonami i s� to jedne z najwa�niejszych proces�w w systemie. B�dziemy si� zajmowa� procesem "init" i demonami w przysz�ych artyku�ach.  

Wielozadaniowo�� w libc

Teraz mamy poj�cie znaczenia poj�cia proces i jego wagi dla systemu. Teraz b�dziemy zajmowa� si� napisaniem kodu, kt�ry b�dzie wykorzystywa� wielozadaniowo��. Od prostej sytuacji uruchomienia procesu b�dziemy si� przesuwa� w kierunku nowych problem�w: komunikacji pomi�dzy procesami i ich synchronizacj�. Znajdziemy dwa bardzo dobre rozwi�zania tego problemu: komunikaty i semafory - kt�re zostan� p�zniej szczeg�owo opisane w jednym z nast�pnych artyku��w dotycz�cym w�tk�w. Po "komunikatach" zabierzemy si� za pisanie naszej aplikacji bazuj�c na wszystkim o czym by�a do tej pory mowa.

Standardowa biblioteka C (libc, zaimplementowana w Linux'ie jako glibc) u�ywa narz�dzi z systemu Unix V, system Unix V (teraz SysV) jest komercyjn� implementacj� Unixa i tak�e zapocz�tkowa� jedn� z najwa�niejszych rodzin Unixa, inne stanowi� rodzin� BSD Unix.

W libc typ danych pid_t jest zdefiniowany jako integer zawieraj�cy numer pid. Od teraz b�dziemy u�ywa� tego do uzyskania numeru pid, ale tylko dla jasno�ci, u�ywanie integera to samo.

Poka�my teraz funkcj�, kt�ra daje nam wiedze na temat numeru pid naszego programu.

pid_t getpid (void)

(funkcja ta jest zdefiniowana razem z pid_t w unistd.h i sys/types.h). Napiszemy program, kt�ry zademonstruje nam dzia�anie funkcji getpid() i wypisze nasz pid na standardowym wyj�ciu (na ekranie).

#include <unistd.h>;
#include <sys/types.h>;
#include <stdio.h>;

int main()
{
  pid_t pid;

  pid = getpid();
  printf("The pid assigned to the process is %d\n", pid);

  return 0;
}
Zapisz program jako print_pid.c i skompiluj to.
gcc -Wall -o print_pid print_pid.c
Stworzyli�my plik wykonywalny print_pid. Przypominam, �e je�li katalog bie��cy nie jest w �cie�ce to wymagane jest uruchomienie programu jako "./print_pid". Wynik naszego programu nie b�dzie wielkim zaskoczeniem : wypisze dodatni� liczb� i je�li uruchomimy ten program wi�cej ni� raz, zobaczymy, �e ten numer b�dzie zwi�kszony o jeden raz za razem, wzrost ten nie musi by� zawsze taki sam, poniewa� mo�e wyst�pi� przypadek, �e pomi�dzy dwoma uruchomieniami naszego programu mog� zosta� uruchomione inne procesy co spowoduje zwi�kszenie si� naszego numery o inn� warto��. Na przyk�ad mo�emy uruchomi� ps pomi�dzy dwoma uruchomieniami print_pid.

Teraz nadszed� czas aby nauczy� si� tworzy� procesy. Kiedy program (zawieraj�cy si� w procesie A) tworzy inny proces (B) oba s� identyczne, to znaczy oba maj� ten sam kod, oba wykorzystuj� w tym samym stopniu pami�� i oba maj� ten sam status procesora. Od tego momentu oba mog� by� wykonywane w dwa r�ne sposoby, na przyk�ad w zale�no�ci od wyj�cia u�ytkownika albo innych zdarze� losowych. Proces "A" jest "procesem nadrz�dnym", a proces "B" jest "procesem podrz�dnym". Teraz mo�emy lepiej zrozumie� nazw� "ojciec wszystkich proces�w", czyli procesu nadrz�dnego do wszystkich co si� odnosi�o do procesu init. Funkcj�, kt�ra tworzy nam inne procesy jest funkcja:

pid_t fork(void)
nazwa ta pochodzi od w�asno�ci proces�w jak� jest rozwidlanie. Funkcja ta zwraca numer pid, ale zas�uguje na szczeg�ln� uwag�. Powiedzieli�my sobie, �e nasz proces duplikuje si� na na proces nadrz�dny i podrz�dny, kt�re s� wykonywane poprzez przeplatanie z innymi uruchomionymi procesami, robi�c inn� prac�. Nasuwa si� pytanie, kt�ry z proces�w b�dzie wykonywany jako pierwszy: nadrz�dny czy podrz�dny. Odpowiedz jest prosta: pierwszy albo drugi. Decyzja, kt�ry proces b�dzie uruchomiony jako pierwszy, jest brana z harmonogramu systemu operacyjnego i nie ma znaczenia czy jest to proces nadrz�dny czy podrz�dny, kolejno�� zale�y od algorytmu zale�nego od wielu parametr�w.

Jednak�e istotne jest �eby wiedzie�, kt�ry proces si� w�a�nie wykonuje poniewa� kod jest taki sam, oba procesy b�d� zawiera� kod procesu nadrz�dnego i podrz�dnego ale ka�dy z proces�w b�dzie wykonywa� jedna wybrana cz��. Najlepiej przedstawi to poni�szy algorytm :

- FORK
- IF YOU ARE THE SON EXECUTE (...)
- IF YOU ARE THE FATHER EXECUTE (...)
co reprezentuje w pewnym abstrakcyjny spos�b kod naszego programu. Funkcja fork zwraca '0' do procesu podrz�dnego i pid procesu podrz�dnego do procesu nadrz�dnego. Je�li zwr�conym pid jest zero to wiemy kt�ry proces b�dzie wykonywany. W j�zyku C wygl�da to tak :
int main()
{
  pid_t pid;

  pid = fork();
  if (pid == 0)
  {
    CODE OF THE SON PROCESS
  }
  CODE OF THE FATHER PROCESS
}
Nadszed� czas na napisanie pierwszego przyk�adu wykorzystania wielozadaniowo�ci, nasz kod mo�emy zapisa� jako fork_demo.c i skompilowa� jak by�o to robione poprzednio. Ponumerowane linie wstawi�em �eby u�atwi� omawianie programu. Program ten rozdzieli proces na nadrz�dny i podrz�dny, oba procesy wypisz� co� na ekranie (je�li wszystko p�jdzie dobrze):
(01) #include <unistd.h>
(02) #include <sys/types.h>
(03) #include <stdio.h>

(04) int main()
(05) {
(05)   pid_t pid;
(06)   int i;

(07)   pid = fork();

(08)   if (pid == 0){
(09)     for (i = 0; i  < 8; i++){
(10)       printf("-SON-\n");
(11)     }
(12)     return(0);
(13)   }

(14)   for (i = 0; i < 8; i++){
(15)     printf("+FATHER+\n");
(16)   }

(17)   return(0);
(18) }

Linie (01)-(03) zawieraj� informacje o plikach nag��wkowych, z kt�rych korzysta nasz program (standard I/O, multitasking).
Funkcja main (jak zawsze w GNU), zwraca integera, kt�ry normalnie jest r�wny zero je�li wszystko posz�o dobrze i program wykona� si� w ca�o�ci bez �adnych b��d�w albo co� posz�o nie tak i wyst�pi�y b��dy. Przypu��my, �e wszystko posz�o dobrze, bez b��d�w (kontrole b��d�w mo�na doda� jak wszystko b�dzie dobrze w podstawowym programie). W linii (05) definiujemy typ danych pid i zmienn� i (06) potrzebn� nam do obs�ugi p�tli. Te dwa typy s� takie same ale dla jasno�ci s� zdefiniowane inaczej.
W linii (07) wywo�ujemy funkcj� fork, kt�ra gdy zwr�ci zero spowoduje wykonanie procesu podrz�dnego da nam pid procesu podrz�dnego. Sprawdzane to jest w linii (08). Nast�pnie wykonywany jest kod programu w liniach (09)-(13) nale��cy do procesu podrz�dnego nast�pnie reszta dotycz�ce procesu nadrz�dnego,czyli linie (09)-(13).
Dwie cz�ci po prostu wypisuj� 8 razy na standardowym wyj�ciu s�owo "-SON-" albo "+FATHER+", w zale�no�ci od tego kt�ry proces jest wykonywany i na ko�cu zwraca zero. To jest bardzo wa�ne, poniewa� bez tego ostatniego "return" proces podrz�dny ko�cz�c swoj� p�tle m�g�by wykonywa� kod przeznaczony dla procesu nadrz�dnego (mo�na tego spr�bowa�, to nie zniszczy ci komputera, ale po prostu my tego nie chcemy). Taki b��d b�dzie naprawd� bardzo ci�ko znalez�, ponowne uruchamianie programu (zw�aszcza bardziej z�o�onego) b�dzie dawa� nam inne wyniki, w wyniku czego usuni�cie b��d�w b�dzie prawie ca�kiem niemo�liwe.

Uruchamiaj�c ten program mo�liwe, �e b�dziesz nie usatysfakcjonowany. Ja nie mog� zapewni�, �e rezultat b�dzie mieszanin� tych dw�ch �a�cuch�w , zale�ne to b�dzie od pr�dko�ci wykonania tej kr�tkiej p�tli. Prawdopodobnie tw�j ekran b�dzie wypisywa� �a�cuchy "+FATHER+" i "-SON-", albo przeciwnie. Spr�buj uruchomi� program wi�cej ni� jeden raz i zobaczysz �e rezultaty mog� ulec zmianie.

Wstawiaj�c losowe przerwy pomi�dzy ka�dym printf mo�emy zauwa�y� wi�cej wizualnych efekt�w. U�yjemy do tego funkcji sleep i random.

sleep(rand()%4)
ta linijka sprawia, �e program "usypia" na losowo wygenerowan� liczb� sekund pomi�dzy 0 i 3 (operator % zwraca reszt� z dzielenia liczby integer), teraz kod wygl�da nast�puj�co:
(09)  for (i = 0; i < 8; i++){
(->)    sleep (rand()%4);
(10)    printf("-FIGLIO-\n");
(11)  }
i robimy to samo dla kody procesu nadrz�dnego. Zapisujemy jako fork_demo2.c, kompilujemy i uruchamiamy. Teraz program wykonuje si� wolniej, ale tak�e widzimy r�nice w tym co otrzymujemy na wyj�ciu:
[leo@mobile ipc2]$ ./fork_demo2
-SON-
+FATHER+
+FATHER+
-SON-
-SON-
+FATHER+
+FATHER+
-SON-
-FIGLIO-
+FATHER+
+FATHER+
-SON-
-SON-
-SON-
+FATHER+
+FATHER+
[leo@mobile ipc2]$

Teraz przyjrzyjmy si� problemowi, jaki tu mamy. Tworzymy pewn� ilo�� proces�w podrz�dnych tak ,�e wykonuj� operacje inne ni� te wykonywane przez proces nadrz�dny w r�wnoczesnym �rodowisku obliczeniowym, czasami proces nadrz�dny potrzebuje skomunikowa� si� z procesem podrz�dnym albo przynajmniej zsynchronizowa� si� z nim aby wykona� operacje w odpowiednim czasie. Pierwszym sposobem aby uzyska� tak� synchronizacj� jak funkcja wait:

pid_t waitpid (pid_t PID, int *STATUS_PTR, int OPTIONS)
gdzie PID jest numerem PID procesu kt�ry ma czeka�, STATUS_PTR jest wskaznikiem na liczb� integer, kt�ra okre�la status procesu podrz�dnego (NULL jest wtedy je�li informacje nie s� wymagane) i OPTIPNS jest zbiorem opcji, o kt�re na razie nie musimy si� martwi�. To jest przyk�ad programu w kt�rym proces nadrz�dny tworzy proces podrz�dny i czeka a� on si� sko�czy :
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>

int main()
{
  pid_t pid;
  int i;

  pid = fork();

  if (pid == 0){
    for (i = 0; i < 14; i++){
      sleep (rand()%4);
      printf("-SON-\n");
    }
    return 0;
  }

  sleep (rand()%4);
  printf("+FATHER+ Waiting for son's termination...\n");
  waitpid (pid, NULL, 0);
  printf("+FATHER+ ...ended\n");

  return 0;
}
Funkcja sleep w kodzie procesu nadrz�dnego zosta�a wstawiona aby rozr�ni� wykonanie. Zapiszmy kod jako fork_demo3.c, skompilujmy i wykonajmy. W�a�nie napisali�my nasz pierwsz� aplikacj� wykorzystuj�c� wielozadaniowo�� systemu linux!

W nast�pnym artykule nauczymy si� wi�cej na temat synchronizacji i komunikacji pomi�dzy procesami. Teraz napisz sw�j program u�ywaj�c poznanych funkcji i jak chcesz to wy�lij mi a ja sprawdz� czy dobrze ich u�y�e� i je�li b�d� b��dy to je poprawie. Wy�lij mi plik z kodem oraz ma�y plik tekstowy opisuj�cy program, twoje Imi� i e-mail. Powodzenia!  

Warto przeczyta�

 

Dyskusja dotycz�ca tego artyku�u

Komentarze do dyskusji:
 Strona talkback 

Strona prowadzona przez redakcj� LinuxFocus
© Leonardo Giordani, FDL
LinuxFocus.org
t�umaczenie:
it --> -- : Leonardo Giordani <leo.giordani(at)libero.it>
en --> pl: Przemyslaw Pozniak <malinaz(at)poczta.wp.pl>

2002-11-24, generated by lfparser version 2.33