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:
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:
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:
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.