Johdanto

Tietokone koostuu monista eri komponenteista: CPU, muisti, kiintolevy, verkkokortti, näyttö, näppäimistö, hiiri. Kukaan ei voi hallita kaikkea tekniikkaa hyvin, siksi koneet rakentuvat hierarkkisten tasojen päälle.

Alimpana rauta joka sekin muodostuu useasta kerroksesta, rautaa ohjelmoidaan konekielellä. Raudan ja sovellusten välillä käyttöjärjestlemä. Käyttöjärjestelmän lisäksi koneissa systeemiohjelmistoa kuten kääntäjät, editorit, komentotulkki, ikkunointiympäristö.

Käyttöjärjestelmällä on kaksi tehtävää:

  • rajapinta raudan ja sovellusten välillä
  • koneen resurssien haltija
  • Käyttöjärjestelmä käyttäjärajapintana

    Olisi erittäin hankalaa, jos jokainen sovellusohjelmoija joutuisi huolehtimaan oheislaitteiden ohjaamisesta. Käyttöjärjestelmä tarjoaa miellyttävämmän ja helppokäyttöisen käyttörajapinnan koneen laitteisiin.

    Esimerkiksi levylle talletetut tiedot ovat vain joukkoja bittejä, joista tieto koodataan magnetoimalla levypintaa. Käyttöjärjestelmä tarjoaa käyttäjälle näkymän missä levylle talletettava tieto organisoidaan tiedostojen ja hakemistojen avulla.

    Sovellus käsittelee tiedostoja systeemikutsujen (esim. open, read, write, close) tai kirjastorutiinien (esim. fopen, fgets, fputs, fwrite, wread, fclose) avulla. Sovelluksen käytössä ovat siis tiedostojen tasolla toimivat operaatiot, esim. lue dataa tiedostosta.

    Käyttöjärjestelmä huolehtii laitteiden käyttöön liittyvistä yksityiskohdista, eli esim. mihin kohtaan levyä tietyn tiedoston bitit ovat tallentuneet. Sovellusohjelman kannalta ei ole edes väliä minkä valmistajan laitteesta on kyse.

    Käyttöjärjestelmä siis tarjoaa rajapinnan, jonka päälle on huomattavasti helpompi rakentaa sovelluksia kuin suoraan raudan päälle.

    Systeemikutsut

    Systeemikutsut muodostavat rajapinnan jonka kautta sovellusohjelmat kutsuvat KJ:n palveluita. Systeemikutsurajapinta riippuu KJ:stä. Vaikka rajapinnoissa on eroja, tarjoavat lähes kaikki KJ:t suunnilleen samat palvelut. Kun ohjelma suorittaa jonkin systeemikutsun, siirrytään suorittamaan KJ:n koodia.

    Etuoikeuttettu tila

    Systeemikutsun suorittaminen aiheuttaa keskeytyksen, jonka seurauksena prosessori siirtyy etuoikeutettuun tilaan ja ruvetaan suorittamaan KJ:n koodia. Laitteistoa käsittelevät ja muut koneen toiminnan kannalta kriittiset käskyt ovat sellaisia, että ne voidaan suorittaa vain prosessorin ollessa etuoikeutetussa tilassa. Prosessori on eutoikeutetussa tilassa ainoastaan suorittaessaan käyttöjärjestelmän koodia, tämä takaa sen, että tavallinen ohjelma ei voi sotkea koneen toimintaa.

    Systeemikutsu (esim. tiedoston lukeminen) saattaa aiheuttaa sen, että kutsuva ohjelma (tai prosessi niinkuin suorituksessa olevaa ohjelmaa kutsutaan) jää odottamaan kutsun aiheuttaman toimenpiteen valmistumista. Tässä tapauksessa kutsun tehnyt ohjelma siirtyy pois suorituksesta ja suoritukseen valitaan joku toinen ohjelma. Kun systeemikutsun toimenpide on suoritettuna, voidaan kutsun tehnyt ohjelma taas tuoda jossain vaiheessa suoritukseen.

    Unixissa systeemikutsuja on noin 100-200 kpl. Koska Unixeista on useita versioita (mm. Linux) on olemassa standardoitu POSIX-rajapinta, jota kaikki UNIX:it pyrkivät noudattamaan. Näin eri UNIX:ien versioista on saatu yhteensopivia.

    Seuraavassa joitakin systeemikutsuja:

    Ohjelmoija ei käytä välttämättä suoraan systeemikutsuja. Esim. Javassa ohjelmoija käyttää KJ:n tarjoamia palveluja epäsuoraan Java API:n kautta. Samoin esim. C ja C++ tarjoavat ohjelmoijalle korkeamman tason kirjastorutiineja. Kirjastorutiinien toteutus käyttää systeemikutsuja.

    Tutustumme kurssin aikana Linuxin tarjoamaan systeemikutsurajapintaan sekä GNU C -kirjastoon. Koska C on Linuxien ja UNIX:ien äidinkieli, ohjelmoimme kurssilla käyttäen C-kieltä. Koska C on C++:n osajoukko, ovat kaikki palvelut suoraan C++-ohjelmien käytössä.

    Linuxin käyttö ohjelmointialustana on kasvussa. Linux on tulossa myös mobiilimaailmaan. Motorolan kännykät toimivat jo suurimmaksi osaksi Linux-alustalla toimivia. Myös Nokia on siirtymässä Linuxin käyttöön, N770 ja N800 -mallien myötä

    Käyttöjärjestelmä resurssien hallitsijana

    Rauta voidaan ajatella resursseina, joita ohjelmat tarvitsevat suorituksensa aikana. Eri resursseja ovat esim. prosessori, muisti, kiintolevy, printteri. Käyttöjärjestelmän tehtävänä on huolehtia koneen resurssien käytöstä

    Koneilla pyörii yhtäaikaisesti monta ohjelmaa, eli käytössä on moniajo. Prosessoreja on usein vain yksi, eli prosessoriaikaa on jaettava ohjelmien kesken. Nykyään on tietysti olemassa moniytimisiä prosessoreja, mutta ongelma on sama, ohjelmia voi olla käynnistettynä enemmän kun koneessa on suoritusyksiköitä.

    Koneen muistista pitää varata tilaa jokaiselle suoritettavalle ohjelmalle. Usean käyttäjän ympäristössä on huolehdittava suojauksesta. Vain omistaja saa pääsee käsiksi omiin tiedostoihinsa. On myös huolehdittava siitä, että yksittäinen ohjelma ei pääse sotkemaan muita koneella pyöriviä ohjelmia.

    Resursseja jaettava tehokkaasti. Esim. tiedon lukeminen kiintolevyltä on tietokoneen mittakaavassa hidasta. Jos jokin ohjelma tekee levyhaun, ei kannata tuhlata CPU-aikaa tiedon odottamiseen. Odotusaikana kannattaa suorittaa CPU:lla jotain muuta ohjelmaa.

    Resurssien jakelussa oltava reilu. Jos koneella yhtä aikaa useita sovelluksia, ei mitään saa laiminlyödä, vaan suoritusaikaa on jaettava kaikille. Jos useita suoritettavia ohjelmia (esim. mp3-soitin, ja www-selain), on kaikille annettava CPU-aikaa riittävän usein. Suorituksessa olevan ohjelman vaihtaminen vie kuitenkin jonkin verran CPU:aikaa. Eli mitä useammin suoritettavaa ohjelmaa vaihdetaan, sitä enemmän CPU-aikaa menee hukkaan. Toisaalta suorituksessa olevaa ohjelmaa on pakko vaihtaa usein, jotta käyttö on järkevää: esim. mp3-soittimen on saatava niin usein suoritusvuoro, että musiikki kuulostaa katkeamattomalta.

    Oheislaitteiden hallinta

    Oheislaitteet liittyvät koneeseen laiteohjainten (device controller) välityksellä. Käyttöjärjestelmässä sijaitsevat laiteajurit (device driver) hoitavat laitteiden kanssa kommunikoinnin. Laiteajuri sisältää koodin, joka osaa käsitellä laitetta eli kommunikoida laiteohjaimen kanssa. Jokaiselle laitteelle tarvitaan siis oma ajuri. Kun ohjelma esim. lukee CD-asemalta dataa, tekee ohjelma systeemikutsun joka taas aiheuttaa CD-aseman laiteajurin rutiinien suorituksen. Kun laiteajuri on hoitanut datan siirtämisen laitteelta, palataan jälleen suorittamaan normaalia ohjelmakoodia.

    Muistin hallinta

    Moniajokäyttöjärjestelmän muistissa on kerrallaan useampia ohjelmia. On huolehdittava siitä, että ohjelmat eivät sotke muiden ohjelmien, erityisesti KJ:n muistialuetta. Ohjelman sijaintipaikka fyysisessä muistissa vaihtelee riippuen siitä mitä muuta koneella tehdään samaan aikaan. Käyttöjärjestelmän on huolehdittava siitä, että ohjelmoijan ei tarvitse tietää mitään siitä, missä kohtaa muistia ohjelma milloinkin sijaitsee.

    Muistinhallinta toteutetaan yleensä käyttäen virtuaalimuistia. Ohjelmat luulevat toimivansa kokonaan omassa, yhtenäisessä muistialueessaan. Yksittäinen ohjelma ei näe muiden ohjelmien muistialuetta (ellei näin haluta tapahtuvan). Käyttöjärjestelmä sijoittelee ohjelmien muistialueet eri puolille koneen fyysistä muistia. Voi olla, että osa muistialueista sijoitetaan tilapäisesti levylle ns. swap-alueelle. Kaikki tämä tapahtuu koneen käyttäjän, ohjelman ja sovellusohjelmoijan kannalta huomaamattomasti.


    Ohjelmointi Linux-ympäristössä

    Editori

    Ohjelma kirjoitetaan sopivalla tekstieditorilla. Nano on helppokäyttöinen mutta ei ominaisuuksiltaan kovin kummallinen. Ohje nanon käyttöön.

    Ohjelmoijan kannalta paras editori on Emacs. Ohjeita emacsin käyttöön:

  • lista komennoista
  • Jukka Korpelan emacs-opas
  • Käännös

    Jos ohjelmakoodi on kirjoitettu tiedostoon testi.c tapahtuu käännös antamalla komento gcc testi.c. Jos kaikki menee hyvin, tuloksena on tiedosto a.out joka on suoritettavissa oleva ohjelma. Ohjelma suoritetaan komennolla ./a.out

    Suoritettavalle ohjelmalle voidaan antaa jo käännösvaiheessa joku järkevämpi nimi antamalla komento gcc testi.c -o testiohj. Näin syntyy suoritettava ohjelmatiedosto nimeltä testiohj.

    Joskus käännöksessä tarvitaan parametrejä. Esim. matematiikkakirjastoa käyttävä ohjelma käännetään antamalla parametri -lm: gcc testi.c -o testiohj -lm

    Kääntäjä antaa varoituksia (warning) epäilyttävistä koodiriveistä käyttämällä parametria -Wall: gcc testi.c -o testiohj -Wall. Varoitusten mukaan ottaminen käännökseen on järkevää.

    Jos kaikki koodi ei ole yhdessä tiedostossa tai käännösparametreja on paljon, kannattaa käyttää make:a ja Makefile:ä. Lisää make-työkalusta.

    Debuggerin käyttö on isommissa ohjelmointiprojekteissa lähes välttämätöntä. Ohjeita GNU-debuggerin eli gdb:n käyttöön:

  • tärkeimmät käskyt
  • GDB-pikaopas
  • gcc-, gdb- ja make-ohje
  • Gdb:n online-manuaali
  • Dokumentaatio

    Erittäin tärkeä tietolähde Linux-ohjelmoijalle ovat man-sivut. Man-sivut jakautuvat ns. sektioihin. Ohjelmoijaa kiinnostavat lähinnä sektiot 2 ja 3. Sektiosta 2 löytyvät Linuxin systeemikutsut ja sektiosta 3 C-kirjastofunktiot.

    Jos etsitään tietoa esimerkiksi funktiosta open, on oltava tarkkana. Jos annetana käsky man open tulostuu seuraava:


    OPEN(1)                            Linux 1.x                           OPEN(1)
    
    NAME
           open - start a program on a new virtual terminal (VT).
    
    SYNOPSIS
           open [-c vtnumber] [-s] [-u] [-l] [-v] [--] command command_options
    
    DESCRIPTION
           open  will find the first available VT, and run on it the given command
           with the given command options, standard input, output  and  error  are
           directed  to  that terminal. The current search path ($PATH) is used to
           find the requested command. If no command is specified then  the  envi-
           ronment variable $SHELL is used.
    


    Tämä ei kuitenkaan ole ohjelmoijan etsimä man-sivu. Sivun ylälaidassa lukee OPEN(1). Tämä tarkoittaa, että kyseessä on sektion 1 man-sivu. Sektio 1 sisältää komentotulkin komentojen man-sivut. Oikea man-sivu saadaan esille komennolla man 2 open:


    OPEN(2)                          System calls                          OPEN(2)
    
    NAME
           open, creat - open and possibly create a file or device
    
    SYNOPSIS
           #include < sys/types.h>
           #include < sys/stat.h>
           #include < fcntl.h>
    
           int open(const char *pathname, int flags);
           int open(const char *pathname, int flags, mode_t mode);
           int creat(const char *pathname, mode_t mode);
    
    DESCRIPTION
           The  open()  system  call  is  used  to  convert a pathname into a file
           descriptor (a small, non-negative integer for use in subsequent I/O  as
           with  read,  write,  etc.).   When  the  call  is  successful, the file
           descriptor returned will be the lowest file  descriptor  not  currently
           open  for  the  process.  This call creates a new open file, not shared
           with any other process.  (But shared  open  files  may  arise  via  the
           fork(2)  system  call.)   The new file descriptor is set to remain open
    


    Jos tarvitaan tietoa kirjastofunktiosta, on etsittävä man sivujen sektiosta 3. Esim. printf-komennon tiedot löytyvät sektiosta 3. Komennolla man printf saadaan väärät tiedot, mutta man 3 printf tuottaa oikean tuloksen:


    PRINTF(3)                  Linux Programmer's Manual                 PRINTF(3)
    
    NAME
           printf,   fprintf,  sprintf,  snprintf,  vprintf,  vfprintf,  vsprintf,
           vsnprintf - formatted output conversion
    
    SYNOPSIS
           #include < stdio.h>
    
           int printf(const char *format, ...);
           int fprintf(FILE *stream, const char *format, ...);
           int sprintf(char *str, const char *format, ...);
           int snprintf(char *str, size_t size, const char *format, ...);
    
           #include < stdarg.h>
    
           int vprintf(const char *format, va_list ap);
           int vfprintf(FILE *stream, const char *format, va_list ap);
           int vsprintf(char *str, const char *format, va_list ap);
           int vsnprintf(char *str, size_t size, const char *format, va_list ap);
    
    DESCRIPTION
           The functions in the printf family produce output according to a format
    


    Joskus man-sivuihin viitataan merkinnällä printf(3), joka siis tarkoittaa printf-komennon sektion 3 man-sivua, tai open(2), eli open-komennon sektion 2 man-sivu. Vaikka man-sivujen teksti vaikuttaa aluksi ehkä hankalalta ymmärtää, kannattaa man-sivujen käyttöön tottua.

    Toinen erinomainen tietolähde on GNU C -kirjaston online-dokumentaatio. GNU-dokumentaatio on paikoin man-sivuja helppolukuisempaa ja sisältää joitakin koodiesimerkkejä. Myös C/C++ Refefence sivuilla hyvää materiaalia C standardifunktioista.

    Linux-alustalla ohjelmoinnista on olemassa suuri määrä kirjoja. Tämän kurssin materiaaliin ovat vaikuttaneet ainakin seuraavat kirjat:

  • Tim Jones: GNU/Linux application programming
  • John Fusco: The Linux programming
  • Arnold Robbins: Linux programming by example
  • Neil Matthews, Richard Stones: Beginning Linux programming
  • Michael Johnson, Eric Troan: Linux application development
  • Richard Stevens: Advanced programming in Unix environment