ITKA203 Käyttöjärjestelmät, luento 9/14, Ke 15.6.2011 ===================================================== Info: - Demotilaisuudet jatkuvat heti luennon jälkeen (luokka Latin/Agora) - Demo 3 julkaistaan illalla. Pieni jatko ja yhteenveto eiliseen: ----------------------------------- Eilen nähtiin muutama tapa prosessien/säikeiden kommunikointiin: - signaalit (asynkronisia eli "milloin tahansa saapuvia" ilmoituksia esim. virhetilanteista tai lopetus-/odottelupyynnöistä; ohjelma voi valmistautua käsittelemään, rekisteröimällä käyttöjärjestelmäkutsun avulla käsittelijäaliohjelman) - viestit (datapuskuri eli muistialueen sisältö, joka voidaan lähettää prosessilta toiselle viestijonoa hyödyntäen) - jaetut muistialueet (muistialue, jonka käyttöjärjestelmä pyydettäessä liittää osaksi kahden tai useamman prosessin virtuaalista muistiavaruutta) Nimien ja toimintaperiaatteen tasolla vielä lisää IPC-menettelyjä: - putket (putken käyttöä nähty mm. demossa, esim:: ``ps -ef | grep bash | grep `whoami` `` ; käyttöjärjestelmä hoitaa putken operoinnin eli käytännössä tuottaja-kuluttaja -tilanteen hoidon; prosessi voi lukea putkesta tulevan datan standardisisääntulostaan, ja standardiulostulo voidaan yhdistää ulospäin menevään putkeen. Esim. Javassa nämä on kapseloitu olioihin System.in ja System.out - postilaatikko (prosessi laittaa asioita "laatikkoon" ja yksi tai useampi voi käydä sieltä lukemassa) - portti (kahden prosessin välinen postilaatikko) - RPC, remote procedure call, etäaliohjelmakutsu (parametrit hoidetaan toisen prosessin sisältämän aliohjelman käyttöön ja paluuarvo palautetaan kutsujalle; voidaan tehdä myös verkkoyhteyden yli eli voi kutsua vaikka eri tietokoneessa olevaa aliohjelmaa) Sopivalla lukitus- ja jonotusmenettelyllä viestejä ja postilaatikkoa voidaan käyttää synkronointiin kuten eilen nähtyjä semaforeja. Eilen nähtiin tyypillisiä ongelmia jaetun resurssin käytössä: - poissulkeminen (mutual exclusion; tarpeen, jos resurssi voi mennä sekaisin samanaikaisesta käytöstä) - tuottaja-kuluttaja -tilanne (sisältää poissulkemisen, ja lisäksi tuottamisen ja kuluttamisen odottelun, jos toinen osapuoli ei ole vielä tehnyt osaansa loppuun tietoalkion osalta.) Tutustuttiin nyt tarkemmin vielä yhteen: - deadlock eli prosessien tahaton lukittuminen Maininnan tasolla vielä yksi: - kirjoittajat-lukijat -tilanne (kuten tuottaja-kuluttaja, mutta mahdollisesti useita kirjoittajia ja/tai lukijoita) Huomio Javasta ym. "hienoista alustoista": Synkronointi edellyttää toimenpiteitä ohjelmoijalta. Tarkkana! Vilkaistiin Alfonse ja Gaston -esimerkki (linkki ohjelmiin kurssin sivulta tämän luennon kohdalta) Tämän päivän aihe: Muistinhallinta ---------------------------------- Muistin organisoinnista, "muistihierarkia": - Rekisterit - Välimuistit (Level 1, Level 2; prosessoriteknologia hoitaa välimuistin käytön; ohjelma näkee virtuaalimuistiavaruuden) - Keskusmuisti - Massamuistit kuten kovalevyt Hinta vs. Koko -- Hinta vs. Nopeus. Mitä lähempänä prosessoria sen nopeampaa, mutta kalliimpaa. Mahdoton saavuttaa täydellistä tilannetta; tarvitaan kompromissi. Niinpä kalliita ja nopeita rekisterejä voidaan järjestelmään suunnitella muutamia, Level 1:n välimuistia jonkin verran ja Level 2 (ja ehkä Level 3) -välimuistia vielä vähän enemmän. Suunnittelu- ja tuotantokustannukset mutta samalla muistien käytön nopeus putoavat sitä mukaa kuin etäisyys prosessoriin kasvaa. Keskusmuistia on nykyään aika paljon, mutta ohjelmatkin tuppaavat kehittymään siihen suuntaan että niiden uudemmat ja hienommat versiot lopulta käyttävät niin paljon keskusmuistia kuin kulloisellakin aikakaudella on saatavilla. Massamuistit ovat äärimmäisen halpoja, mutta myös keskusmuistiin nähden äärimmäisen hitaita. Taustaa sivuttavalle virtuaalimuistille: - Aikoinaan oli hyvin vähän keskusmuistia (muisti kallista). Prosesseja halutaan suorittaa enemmän kuin keskusmuistiin mahtuu. - Levytilaa on aina ollut paljon enemmän ja edullisesti saatavilla Tästä seuraa tarve jollekin fiksulle järjestelmälle, joka hyödyntää hidasta levytilaa pienen keskusmuistin apuna. Idea: - Lokaalisuusperiaate (principle of locality): suurin osa viittauksista ohjelman virtuaalimuistiin tapahtuu lähelle äskettäin viitattuja muistipaikkoja (esim. koodin osalta peräkkäin suoritettavat käskyt, muutamien käskyjen mittaiset silmukat, usein toistettavat aliohjelmat; pinon osalta yleensäkin käytetään muutamia peräkkäin sijoittuvia aktivaatiotietueita; datan osalta käsitellään suhteellisen pitkään tiettyä oliota/tietorakennetta ennen siirtymistä seuraavaan). - Muistissa voidaankin siis pitää vain äskettäin tai "näinä aikoina" käytetty koodi ja data, työjoukko eli "working set". - Loput tiedot voivat odotella levyllä - "Suspended" -tilassa olevat prosessit voi heittää levylle kokonaan, koska niitä ei tällä hetkellä ylipäätään suoriteta. - Jaetaan prosessin virtuaalimuisti ns. sivuihin (page), jotka ovat tyypillisesti esim. 4096 tavun mittaisia. - Jaetaan fyysinen muisti sivun kokoisiin kehyksiin (page frame), joissa kussakin voidaan säilyttää yhtä sivua jonkin prosessin "näinä aikoina" tarvitsemaa muistialuetta. - Muut sivut voivat odotella levyllä, että niitä jossain vaiheessa taas tarvitaan. Tarvittavat tietorakenteet (perinteinen perusidea): - Sivutaulu per prosessi; taulussa tiedot per sivu: * "muistibitti" eli onko sivu keskusmuistissa * fyysisen sivun numero, jos sivu on keskusmuistissa * sijainti levyllä (jokaisesta sivusta on levyllä tallessa kopio) - Kehystaulu (yksi kpl) jossa tiedot per keskusmuistin kehys: * minkä prosessin käytössä tämän kehyksen sisältämä sivu on, ja mitä prosessin virtuaalimuistin sivua se vastaa * "kirjoitusbitti" (dirty bit / modified bit): Prosessori asettaa tämän, jos muistiin kirjoitetaan tälle sivulle; sivusta tulee "likainen" siinä mielessä että muistissa oleva versio ei enää ole sama kuin levyllä tallessa oleva. * seinäkelloaika: Prosessori päivittää ajan, kun sivua luetaan/kirjoitetaan * suojaustiedot: Esim. saako sivulle kirjoittaa, saako sieltä noutaa konekäskyjä suoritukseen ("no execute"-bitti); näillä voi jonkin verran rajoittaa perinteisiä tietomurtotapoja. Mitä tämä edellyttää laitteistolta (siis miten prosessoritekniikkaa on pitänyt kehittää että sivuttavaa virtuaalimuistia voi käyttää): - Osoitteenmuodostus aina taulujen avulla, joita itse prosessori osaa käsitellä!! - Sivunvaihtokeskeytys, jolla käyttöjärjestelmä pääsee lataamaan ja tallentamaan sivuja levyltä/levylle. Algoritmeista: - Sivunvaihtokeskeytyksen hoitoon siirtyminen tarkoittaa, että prosessorin käskyssä tuli muistiviittaus prosessin sivulle, joka ei ole tällä hetkellä keskusmuistissa. Sivun sisältö pitää silloin ladata fyysisen muistin vapaaseen kehykseen. Kehystaulussa on operaatioon tarvittavat tiedot. - Jos vapaata kehystä ei ole, pitää ensin valita joku käytössä olevista ja vaihtaa (swap) sen sisältämä sivu puolestaan levylle jemmaan. - Esim. LRU, least-recently-used, eli käyttöajankohdan mukaan kauimmin käyttämättä ollut sivu heitetään levylle ja sen tilalle voi sitten ladata uuden. Levyosoite löytyy omistavan prosessin sivutaulusta. - Kehystaulun tiedot tietysti on päivitettävä vastaavasti. Ja jos jotain heitettiin levylle pois fyysisestä muistista, on kyseisen prosessin sivutaulua myös muutettava. Nykypäivästä: Katsottiin x86-64:n nelitasoinen osoitteenmuodostus: "kartta", "hakemistohakemisto", "hakemisto", "sivutaulu" -- 64-bittisessä muistiosoitteessa on nykyisellään 48 bittiä käytössä, 9 bitin mittaiset pätkät ovat indeksejä, joilla löytyy aina seuraavan tason taulukko ja lopulta sivutaulusta fyysisen kehyksen osoite. Laitteiston yksityiskohdista: Välimuistit, TLB:t auttavat prosessoria hoitamaan osoitteenmuodostuksen tehokkaasti. Moniydinprosessoreissa välimuistien käyttö asettaa tietyt haasteet muistinhallinnalle: Kun sivutaulun tietoja muutetaan yhdessä prosessorissa, se ei välttämättä mitenkään maagisesti välity muiden prosessorien välimuisteihin, vaan käyttöjärjestelmä voi joutua käskyttämään myös ytimien välistä tilanpäivitystä sivunvaihdon yhteydessä.