Syitä sille, että prosessi joutuu pois prosessorilta on kaksi:
Ensimmäisessä tapauksessa prosessi ei voi edetä ennen kun systeemikutsun aiheuttama operaatio on valmiina. Prosessi joka ei voi edetä, on sleeping-tilassa. Jälkimmäisessä tapauksessa prosessi taas voisi periaatteessa edetä jos se saisi CPU-aikaa. Prosessi joka voi edetä mutta ei ole suorituksessa on runnable-tilassa.Käynnissä oleva prosessi on siis kullakin hetkellä jossakin kolmesta tilasta: running, runnable tai sleeping. Prosesseilla on myös muita tiloja, esim. zombie, josta puhuttiin viime kerralla, sekä virtuaalimuistiin (josta puhumme myöhemmin) liittyvät tilat, joissa prosessi on swapattuna pois keskusmuistista.
Alla oleva kuva esittää prosessien tilojen suhteita:
Kun prosessi ei voi jatkaa siirtyy se tilaan sleeping, näin tapahtuu esim. odotetaan näppäimen painallusta komennolla gets(). Kun prosessi on jälleen valmiina suoritukseen, siirtyy se tilaan runnable.
Aika ajoin suorituksessa oleva prosessi vaihdetaan. Syynä prosessin vaihtumiseen voi olla systeemikutsu, joka aiheuttaa prosessin siirtymisen sleeping-tilaan. Prosessi saatetaan vaihtaa myös jos todetaan prosessin jo saaneen riittävästi suoritusaikaa, tällöin prosessi siirretään runnable-tilaan odottamaan seuraavaa suoritusvuoroa.
Käyttöjärjestelmä tallettaa suorituksesta pois siirtyvän prosessin suoritustilanteen siten, että prosessi voidaan taas aikanaan palauttaa suoritukseen siten, että se aloittaa täsmälleen samasta tilanteesta, missä se oli siirtyessään pois suorituksesta.
Suorituksessa olevan prosessin vaihtamisen yhteydessä käyttöjärjestelmän skeduleri siis valitsee suoritettavaksi uuden prosessin. Käynnistettävä prosessi jatkaa täsmälleen samasta kohdasta mihin suoritus jäi silloin kun prosessi siirrettiin edellisen kerran pois running-tilasta.
Kun useita prosesseja on yhtä aikaa ready-tilassa ja prosessoreja on vain yksi, on tehtävä valinta missä järjestyksessä CPU-aikaa jaetaan. Valinnan suorittavalla KJ:n osalla eli skedulerilla on ristiriitaisia tavoitteita. Interaktiivisessa käytössä CPU:lla olevia prosesseja vaihdeltava usein, jotta kaikkien prosessien vasteaika säilyy pienenä. Vasteaika (response time) tarkoittaa aikaa, joka kuluu esim. ikonin klikkaamisesta siihen kunnes kone suorittaa vaaditun toimenpiteen. Prosessin vaihtoon kuluu aikaa, eli liika prosessien vaihtelu syö CPU:n resursseja.
On siis mahdollista, että prosessi siirretään pois suorituksesta sen takia, että prosessi on jo suorittanut KJ:n mielestä riittävän kauan. Mekanismi joka tekee tämän mahdolliseksi on ns. kellokeskeytys (engl timer interrupt). Kellokeskeskeytys on tasaisin aikavälein (esim. 1 millisekunnin välein) tapahtuva keskeytys, joka antaa KJ:lle hetkeksi suoritusvuoron kesken normaalin prosessin suorituksen. Kellokeskeytyksen ansioista KJ pääsee tarkastamaan onko suorituksessa oleva prosessi jo kuluttaunut kaiken sille varatun suoritusajan, eli aikaviipaleen (engl. time slice). Jos näin on tapahtunut, skeduleri valitsee uuden prosessin suoritukseen.
On myös mahdollista, että joku korkean prioriteetin prosessi tulee kesken suorituksen runnable-tilaan, tällöin KJ siirtää suorittavan prosessin runnable-tilaan ja antaa suoritusvuoron korkeamman prioriteetin prosessille.
6-1.c on yksinkertainen ohjelma, joka suorittaa looppia, jossa se tulostaa aika-ajoin ruudulle komentoriviparametrinaan saamaa numeroa:
#define LIMIT 300000000 #define PRINT 10000000 int main(int argc, char *argv[]){ int i, n; if ( argc<2 ) return 0; n = atoi(argv[1]); for ( i=0; i < LIMIT; i++){ if ( i%PRINT==0 ) { fflush(stdout); printf("%d ",n); } } return 0; }Skripti run1 suorittaa ohjelmasta 6-1.c yhtäaikaa viisi instanssia. Skriptin ajaminen demonstroi, että KJ todellakin vuorottelee yhtä aikaa suorituksessa olevia prosesseja. Vaikka kaikille prosesseille annetaan suunnilleen saman verran suoritusaikaa, niin prosessien suoritusjärjestys näyttää käyttäjälle hiukan satunnaiselta. Ei siis kannata olettaa mitään siitä, missä järjestyksessä KJ prosesseja skeduloi.
Interaktiivisessa järjestelmässä skeduloinnin tärkein kriteeri on pitää prosessien vasteajat pieninä. Tämän takia aikaviipale pyritään asettamaan melko pieneksi. Suorituksessa olevan prosessin vaihtaminen (englanniksi context switch) on kuitenkin melko kallis operaatio. Esim. Linuxissa prosessin vaihtoon aikaa kuluu (CPU:sta riippuen) noin 2-10 mikrosekuntia. Jos aikaviipale olisi esim. 10 millisekuntia, tarkoittaa tämä että CPU-ajasta noin 0.1% menisi prosessien vaihtoon. Tämä ei ole kovin paljoa, todellinen kustannus onkin muualla, liittyen välimuistiin ja muistinhallintaan, palataan asiaan myöhemmin.
Aikaviipaleen pienennys siis aiheuttaa sen, että koneen suorittaman hyötyprosessoinnin määrä ei ole suurin mahdollinen. Hyötyprosessoinnin määrä saataisiin maksimoitua tekemällä aikaviipaleista erittäin pitkiä. Tämä kuitenkin aiheuttaisi sen että interaktiivisten ohjelmien vasteaika kasvaisi liian pitkäksi käyttäjän kannalta.
Prosessien skedulointi tapahtuu yleensä siten, että jokaiselle prioriteettitasolle on oma skedulointijono. Suoritusvuoroon valitaan korkeimman prioriteettitason jonon kärjessä oleva prosessi.
Yhden prioriteettitason jonon sisällä prosesseja suoritettaan ns. round-robin -periaatteen mukaan, eli suoritetaan aina jonon kärjessä olevaa prosessia niin kauan kunnes prosessi joko tekee blokkaavan systeemikutsun tai kuluttaa kaiken sille varatun suoritusajan. Kaiken suoritusajan kuluttanut prosessi siirretään jonon perälle ja jonon ensimmäinen prosessi valitaan suoritusvuoroon. Jos korkeimman tason jonossa ei ole yhtään prosessia, katsotaan toisen prioriteettitason jonoa jne. Seuraavassa kuva prioriteettijonoista:
Linuxissa prosessin prioriteetti on sitä korkeampi mitä pienempi numero prioriteettiä kuvaa. Prosessin prioriteeti ei välttämättä säily koko aikaa samana. Prioriteetit siis vaihtuvat dynaamisesti suoritusajan kuluessa. Jos prosessi tekee paljon I/O-operaatioita, nostetaan sen prioriteettia. Jos prosessi kuluttaa aina aikaviipaleensa loppuun tekemättä I/O-operaatioita, lasketaan prosessin prioriteettia. Kun prosessin prioiriteetti muuttuu, siirretään prosessi uuden prioriteettinsä mukaiseen skedulointijonoon.
Ote man-sivulta:
nice - change process priority SYNOPSIS #include < unistd.h> int nice(int inc); DESCRIPTION nice adds inc to the nice value for the calling pid. (A large nice value means a low priority.) Only the superuser may specify a negative increment, or priority increase.Seuraavassa ote ohjelmasta 6-2.c, joka asettaa komentoriviparametrina saadun nice-arvon ja suorittaa paljon aikaa kuluttavan loopin.
#define LIMIT 1000000000 int main(int argc, char *argv[]){ int i, n; float f=1; if ( argc<2 ) return 0; n = atoi(argv[1]); if ( nice(n)==-1 ){ perror("nice epäonnistui"); return 0; } for ( i=0; i < LIMIT; i++){ f = f/3.1234; } printf("prosessi %d nice %d valmis\n", getpid(), n); return 0; }Skripti run2 suorittaa ohjelmasta 6-2.c yhtäaikaa viisi eri instanssia eri nice-arvoilla. Seuraavassa tulostus, jossa ajetaan skripti (run & eli käynnistetään skriptiä suorittava prosessi taustalle) ja tutkitaan ps-komennolla prosessien prioriteettejä:
[luuma@telinux1 vko6]$ run & [luuma@telinux1 vko6]$ ps l Uluuma F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 4 29188 23716 23713 15 0 4444 1332 schedu S pts/105 0:00 -bash 4 29188 23760 23758 15 0 4440 1332 schedu S pts/115 0:00 -bash 4 29188 14951 14949 15 0 4444 1320 wait4 S pts/133 0:00 -bash 4 29188 14993 14948 16 0 4444 1328 wait4 S pts/134 0:00 -bash 0 29188 15039 14951 15 0 11588 5656 schedu S pts/133 0:00 emacs index.html 0 29188 15118 1 39 19 1368 256 - RN pts/134 0:00 a.out 20 0 29188 15119 1 39 15 1384 256 - RN pts/134 0:02 a.out 15 0 29188 15120 1 35 10 1388 256 - RN pts/134 0:04 a.out 10 0 29188 15121 1 30 5 1372 256 - RN pts/134 0:06 a.out 5 0 29188 15122 1 25 0 1376 256 - R pts/134 0:07 a.out 0 0 29188 15124 14993 21 0 3292 1220 - R pts/134 0:00 ps l Uluuma [luuma@telinux1 vko6]$ prosessi 15122 nice 0 valmis prosessi 15121 nice 5 valmis prosessi 15120 nice 10 valmis prosessi 15119 nice 15 valmis prosessi 15118 nice 20 valmis [luuma@telinux1 vko6]$Kenttä PRI kertoo prosessin oikean prioriteetin arvon (mitä korkeampi arvo, sitä matalampi prioriteetti), kenttä NI nice-arvon. Kuten näkyy korkeampi nice-arvo saa aikaan korkeamman prioriteettiarvon eli siis matalamman prioriteetin. Koska skriptin suorittamat prosessit ovat laskentapainoitteisia, on niiden prioriteetti kuitenkin melko alhainen verrattuna komentotulkkia ja emacsia suorittaviin prosesseihin joiden cpu:n kulutus on matalampi.
ps-komennon tulosteen kenttä STAT kertoo prosessin tilan. R tarkoittaa running tai runnable, eli prosessorilla olevaa prosessia ei varsinaisesti erotella. S tarkoittaa sleeping ja Z zombie. RN tarkoittaa running tai runnable -tilassa olevaa matalamman prioriteetin prosessia. Muita mahdollisia merkintöjä ovat:
SW sleeping, swapattynä pois muistista SWN matalan prioriteetin prosessi, sleeping ja poissa muistista SL sleeping, lukittuna muistiin RL running tai runnable, lukittuna muistiin T pysäytetty tai debuggauksen alainenKomennon ps lisäksi suorittavia prosesseja voi tarkkailla konnolla top. man-sivulta (huom. koska kysessä bashin komento, on sivu sektiossa 1):
TOP(1) Linux User's Manual TOP(1) NAME top - display top CPU processes SYNOPSIS top [-] [d delay] [p pid] [q] [c] [C] [S] [s] [i] [n iter] [b] DESCRIPTION top provides an ongoing look at processor activity in real time. It displays a listing of the most CPU-intensive tasks on the system, and can provide an interactive interface for manipulating processes. It can sort the tasks by CPU usage, memory usage and runtime. Most fea- tures can either be selected by an interactive command or by specifying the feature in the personal or system-wide configuration file. See below for more information.top siis näyttää systeemin statuksen prosessien suhteen siten, että tilanne päivitetään kerran sekunnissa. Haluttaessa topin näkyä voidaan päivittää milloin tahansa painamalla välilyöntiä. top-komennolla on suuri määrä komentoriviparametreja sekä sen toimintaan suoritusaikana vaikuttavia komentoja, ks. man-sivu. Seuraavassa top suoritettu skriptin run2 käynnistyksen jälkeen.
20:19:59 up 10 days, 21:16, 53 users, load average: 3,07, 1,30, 0,67 439 processes: 433 sleeping, 6 running, 0 zombie, 0 stopped CPU states: cpu user nice system irq softirq iowait idle total 34,4% 64,1% 1,4% 0,0% 0,0% 0,0% 0,0% cpu00 2,8% 94,3% 2,8% 0,0% 0,0% 0,0% 0,0% cpu01 66,0% 33,9% 0,0% 0,0% 0,0% 0,0% 0,0% Mem: 1280620k av, 1255676k used, 24944k free, 0k shrd, 82264k buff 602952k actv, 114288k in_d, 18376k in_c Swap: 2105272k av, 244452k used, 1860820k free 239460k cached PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME CPU COMMAND 18352 luuma 25 0 256 256 212 R 33,1 0,0 0:01 1 6-2 18351 luuma 29 5 252 252 212 R N 27,9 0,0 0:01 0 6-2 18350 luuma 34 10 252 252 212 R N 25,0 0,0 0:01 1 6-2 18349 luuma 39 15 256 256 212 R N 7,5 0,0 0:00 1 6-2 18348 luuma 39 19 256 256 212 R N 3,7 0,0 0:00 1 6-2 18353 luuma 20 0 1308 1308 760 R 2,3 0,1 0:00 0 top 1 root 15 0 496 460 436 S 0,0 0,0 0:15 1 init 2 root RT 0 0 0 0 SW 0,0 0,0 0:00 0 migration/0 3 root RT 0 0 0 0 SW 0,0 0,0 0:00 1 migration/1 4 root 15 0 0 0 0 SW 0,0 0,0 0:00 1 keventd 5 root 34 19 0 0 0 SWN 0,0 0,0 0:00 0 ksoftirqd/0 6 root 34 19 0 0 0 SWN 0,0 0,0 0:00 1 ksoftirqd/1 9 root 15 0 0 0 0 SW 0,0 0,0 0:00 0 bdflush