Procesi

 

Glavnim konceptom u svakom operacijskom sustavu smatramo proces, kao apstrakciju izvršavanja programa. Sve ostalo ovisno je o ovom konceptu i zbog toga je važno shvatiti što je proces.

 

Sva moderna računala, i moderni OS-ovi mogu raditi više stvari odjednom. Dok izvršava neki korisnikov program, računalo u isto vrijeme može čitati i pisati po disku, zaslonu ili printeru. Također može izršavati čas jedan, čas drugi program, puštajući svakog od njih da radi nekoliko desetinki, ili stotinki sekundi. Preciznije rečeno u bilo kojem trenutku procesor vrti samo jedan program, ali vrlo brzo prebacuje izvršavanje programa sa proceora i vraća ga u stanje čekanja. Ponekad opisujući gornju stvar govorimo o pseudo-paralelizmu kako bi naglasili pravi hardverski paralelizam na višeprocesorksim sustavima.

 

 

Razlika između procesa i programa je jedva zamjetna, ali jako bitna. Da bi lakše razumjeli razliku, napravimo jednu analogiju. Zamislimo kuhara koji peče rođendarsku tortu svojoj kćeri. On ima recept i kuhinju opremljenu sa svim sastojcima: brašnom, jajima i sl.

U računalnom svijetu takav kuhar bio bi procesor, a recept bio program, dok bi sastojci bili ulazni podaci. Zamislimo sad da kuharova kćer utrči u kuhinju plačući jer ju je ubola pčela. Kuhar tada zapamti gdje je stao u svom receptu, i pruži pomoć svojoj kćeri. Gledano u računalnom svijetu vidimo da je procesor bio prebačen sa jednog procesa na proces višeg prioriteta, od kojih svaki ima različiti program. Nakon pružene pomoći kuhar će nastaviti točno od mjeswta gdje je stao.

            Ključna ideja je da je proces aktivnost neke vrste. On ima program, ulaz, izlaz i stanje. Pojedini procesor može dijeliti nekoliko procesa, zajedno sa nekim algoritmom raspoređivanja rada tih procesa na tom procesoru.

 

Kreiranje procesa

 

            Četiri su razloga koji uzrokuju kreiranje procesa:

  1. Inicijalizacija sustava
  2. Poziv sistemskog poziva za kreiranje procesa od strane nekog drugog aktivnog procesa.
  3. Korisnički zahtjev za kreiranje procesa.
  4. Pokretanje neke skripte.

 

Kada se pokreće operacijski sustav, obično se kreira nekoliko procesa. Neki od njih su prvom planu i komuniciraju sa korisnikom i obavljaju nekakav posao za njega, dok su ostali procesi u pozadini, nevezani za pojedinog korisnika, nego za pojedinu funkciju. Procesi koji ostaju u pozadini kako bi rukovali sa aktivnostima poput e-maila, web stranica, printanja i sl. zovu se daemons.

Novi proces može biti kreiran od nekog drugog procesa u bilo koje vrijeme. Npr. ako treba dohvatiti veliki broj podataka preko mreže kako bi se sekvencijalno s njima nešto računalo, uobičajeno je da se kreira novi proces koji bi dohvaćao te podatke preko mreže i spremao ih u nekakvu zajedničku memoriju, dok bi prvi proces uzimao te podatke i računao s njima. Posebno će doći do ubrzanja kod višeprocesorskih sustava kad bi se svaki od tih procesa odvijao na različitim procesorima.

U nekom interaktivnom sustavu korisnik može startati program tipkajući naredbu ili klikom na ikonu. I jednim i drugim načinom starta se novi proces u kojem se izvršava odabrani program.

Zadnji slučaj kad se proces kreira je npr. pokretanje neke skripte. Korisnik može poslati sustavu nekakav zathjev ili skriptu (može i preko 'udaljenih poziva'), nakon čega će operacijski sustav, kad odluči da ima dovoljno resursa za pokretanje idućeg posla, kreirati novi proces i unutar njega pokrenuti posao iz reda zahtjeva.

 

Tehnički gledano, u svim ovim slučajevima, neki postojeći proces sistemskim pozivom kreaira novi proces.

U UNIX-u postoji sam jedan sistemski poziv za kreiranje novog procesa: fork. Ovaj poziv klonira proces koji ga je pozvao. Nakon fork-a dva procesa, roditelj i dijete, imaju istu memorijsku sliku, jednaki vrijednosti u okruženju (enviroment strings) i iste otvorene fileove. Obično proces dijete sa sistemskim pozivom execve ili nekim sličnim poziva neki novi program. Npr. kada korisnik u komandnoj ljusci utipka npr. sort, ljuska sa forkom klonira proces i u procesu dijetetu izvrši sort. Razlog ovog postupka u 2 koraka je da bi se djetetu omogućila manipulacija sa file deskriptorima nakon fork-a, a prije izvrsavanja nekog drugog programa, kako bi se mogle izvršiti redirekcije standardnih ulaza i izlaza.

U Windowsima (pritom se misli na WindowsAPI), koristimo API poziv CreateProcess, koji obavlja i kreiranje novog procesa i učitavanje novog programa u novokreirani proces.

            I u Unix-u i u Windowsima, nakon što se proces kreira i roditelj i dijete imaju vlastiti adresni prostor. Ako jedan od njih promijeni nešto u svom adresnom prostoru, ta promjena nije vidljiva u drugom. U Unix-u je adresni prostor dijeteta kopija adresnog prostora roditelja, dok su u Windowsima ti adresni prostori različiti od početka.

 

Primjer 1: simuliranje komandne ljuske:

U pseudo-jeziku opis ljuske bi mogli dati na ovaj način:

 

ponavljaj{

            ispiši_ prompt;

            učitaj_naredbu(N, P);

            kreiraj_novi_proces();

            roditelj: čekaj dok proces dijete ne završi;

            dijete: pokreni naredbu N sa parametrima P;

}

 

Slijedi kod u C-u pod Unixom, u C-u pod Windowsima te u Javi.

 

 

Primjer 2: specifičnost fork naredbe pod Unixom; primjer da procesi imaju različite adresne prostore.

 

 

Završetak procesa

           

1.      Normalni završetak (dobrovoljno)

2.      Završetak zbog greške (Error exit) (dobrovoljno)

3.      Nepopravljiva greška (Fatal error) (prisilno)

4.      Završen (ubijen) od strane nekog drugog procesa (prisilno)

 

Stanja procesa

 

 

            Svaki proces se može naći u jednom od 3 stanja:

  1. Izvršava se (preciznije koristi CPU u tom trenutku)
  2. Spreman je (izvršiv (runnable) , privremeno zaustavljen jer neki drugi proces koristi procesor)
  3. Blokiran (ne može nastaviti izvršavanje dok se ne dogodi vanjski uvjet)

 

Moguće promjene stanja: 1 -> 2,  2 -> 1, 1 -> 3 i 3 -> 2

 

Hijerarhija procesa i implementacija procesa

 

Kod UNIX operacijskom sustavu, proces i sva njegova djeca te njihovi nasljednici tvore jednu grupu. S druge strane, Windowsi nemaju koncept hijerarhije, već su svi procesi jednaki. Jedino mjesto u kojem postoji nešto nalik hijerarhije je kod kreiranja procesa kad roditelj dobije specijalni znak (tzv. handle) pomoću kojeg može kontrolirati dijete.

 

Operacijski sustav održava tablicu procesa, sa jednim zapisom za svaki proces. Ova tablica se još naziva i process control blocks. Točan popis što se sve nalazi u pojedinom zapisu ovisi o pojedinom OS-u, a obično sadrži informaciju o stanju procesa , programskom brojilu, pokazivaču na stog, alociranoj memoriji, stanju otvorenih fileova i sve ostalo što proces mora pohraniti kada prelazi iz  jednog stanja u drugo.


 

Još neke korisne stvari (Kratki opisi nekih naredbi, za detalje vidjeti man stranice pojedine naredbe ili pogledati u MSDN)

 

Create Process (nalazi se u zaglavlju Windows.h)  ima 10 parametara:

  1. Pokazivač na ime modula koji se želi izvršiti.
  2. Pokazivač na string koji predstavlja komandu liniju koja će se izvršiti.
  3. Pokazivač na strukturu za sigurnosnim atributima procesa.
  4. Pokazivač na strukturu za sigurnosnim atributima za inicijalnu dretvu.
  5. Bit koji kazuje da li novi proces naslijeđuje handle-ove procesa koji ga je kreirao.
  6. Različiti flagovi (npr. error mode, prioritet, i sl.)
  7. Pokazivač na environment.
  8. Pokazivač na radni direktorij novog procesa.
  9. Pokazivač na strukturu koja postavlja inicijalne vrijednosti prozora za novi proces.
  10. Pokazivač na strukturu koja vraća podatke o novo kreiranom procesu kao što su njegov process id i handle novog procesa.

Vraća 0 u slučaju greške.

 

WaitForSingleObject  (nalazi se u zaglavlju Windows.h)  – čeka određeni handle (u našem primjeru je to handle procesa)

 

 

fork, exit, wait, waitpid:

Sistemskim pozivom fork zahtijeva se kreiranje novog procesa. Kada proces koji se trenutno izvodi pokrene novi proces, pokrenuti proces postaje "dijete" procesa "roditelja" koji ga je pokrenuo. Dijete dobija kopije segmenta instrukcija i segmenta podataka od roditelja. U stvari, pošto se segment instrukcija normalno ne mijenja, jezgra može uštediti vrijeme i memoriju tako da postavi taj segment kao zajednički za oba procesa (sve dok ga jedan od njih ne odluči inicijalizirati novim programom). Također, dijete nasljeđuje većinu sistemskih podataka od roditelja.

pid_t fork(void) ;   (zaglavlja unistd.h i sys/types.h)

U ovaj sistemski poziv ulazi jedan proces, a iz njega izlaze dva odvojena procesa ("dijete" i "roditelj") i dobivaju svaki svoju povratnu vrijednost. Proces dijete dobiva rezultat 0, a roditelj dobiva identifikacijski broj procesa djeteta. Ako dođe do greške, vraćena vrijednost je ­-1, a dijete nije ni kreirano. fork nema nikakvih argumenata, pa programer ne može biti odgovoran za grešku već je ona rezultat nemogućnosti jezgre da kreira novi proces zbog nedostatka nekog od potrebnih sredstava.

Dijete nasljeđuje većinu atributa iz segmenta sistemskih podataka kao što su aktualni direktorij, prioritet ili identifikacijski broj korisnika. Manje je atributa koji se ne nasljeđuju:

Dijete se može inicijalizirati novim programom (poziv exec) ili izvoditi poseban dio već prisutnog programa, dok roditelj može čekati da dijete završi ili paralelno raditi nešto drugo. Osnovni oblik upotrebe sistemskog poziva fork izgleda ovako:

if (fork() == 0) {
   posao djeteta
   exit(0);
}
nastavak rada roditelja (ili ništa)
wait(NULL);

 

 

 

void exit(int status) ; 

završava izvođenje procesa koji ga je pozvao (ubija ga). Prje završetka, uredno se zatvaraju sve otvorene datoteke. Ne vraća nikakvu vrijednost jer iza njega nema nastavka procesa. Za status se obično stavlja 0 ako proces normalno završava, a 1 inače. Roditelj procesa koji završava pozivom exit prima njegov status preko sistemskog poziva wait ili waitpid.

pid_t wait(int *statusp) ;
pid_t waitpid(pid_t pid, int *statusp, int options)
zaglavlja : sys/wait.h i sys/types.h

Ovaj sistemski poziv čeka da neki od procesa djece završi (ili bude zaustavljen za vrijeme praćenja), s tim da mu se ne može reči koji proces treba čekati. Vraća identifikacijski broj procesa djeteta koji je završio i sprema njegov status (16 bitova) u cijeli broj na koji pokazuje statusp, osim ako je taj argument NULL. U tom slučaju se status završenog procesa gubi. U slučaju greške (djece nema, ili je čekanje prekinuto primitkom signala) rezultat je ­1.

Postoje tri načina kako može završiti proces: pozivom exit, primitkom signala ili padom sustava (nestanak napajanja ili slično). Na koji je način proces završio možemo pročitati iz statusa na koji pokazuje statusp osim ako se radi o trećem slučaju (vidi man wait).

Ako proces roditelj završi prije svog procesa djeteta, djetetu se dodjeljuje novi roditelj - proces init s identifikacijskim brojem 1. init je važan prilikom pokretanja sustava, a u kasnijem radu većinom izvodi wait i tako "prikuplja izgubljenu djecu" kada završe.

Ako proces dijete završi, a roditelj ga ne čeka sa wait, on postaje proces-zombi (zombie). Otpuštaju se njegovi segmenti u memoriji, ali se zadržavaju njegovi podaci u tablici procesa. Oni su potrebni sve dok roditelj ne izvede wait kada proces-zombi nestaje. Ako roditelj završi, a da nije pozvao wait, proces-zombi dobiva novog roditelja (init) koji će ga prikupiti sa wait.