Labratehtävät


Kaikki tähdellä merkittävät tehtävät ovat kaikille pakollisia. Kaikista labratehtävistä on tehtävä vähintään puolet eli 21.

Viikon n teoriaan liittyvät tehtävät on palautettava viimeistään viikolla n+2


Tunti 1

1.

Tee ohjelma, joka tulostaa sille annetut komentoriviparametrit käänteisessä järjestyksessä. Eli ohjelman tulee toimia seuraavasti:
[luuma@telinux1]$ ./ohjelma eka toka kolmas neljäs
neljäs
kolmas
toka
eka
[luuma@telinux1]$
Apua täältä.

2.

Määrittele struct, jolla on int, float ja merkkijonokenttä. Tee ohjelma, jolla on 3 paikkainen taulukollinen määriteltyä structia. Ohjelma täyttää taulukon (esim. kyselemällä tiedot käyttäjältä), sekä tulostaa taulukon sisällön. Apua täältä.

3.

Tee ohjelma, joka sisältää osoittimen tehtävässä 2 määritellyn tyyppiseen structiin. Ohjelma varaa tilaa dynaamisesti structille ja lukee structille sisällön.

Täältä löytyy avustusta. Ja jos pointterien toiminta on unohtunut, on syytä palauttaa asiat mieleen esim. c-kurssin materiaalista. Tällä kurssilla ei nimittäin pointterien (ja struktien) käyttöä voi välttää.

4.

Laajenna tehtävän 3 ohjelmaa siten, että dynaaminen tilanvaraus tehdäänkin kolmelle structille (siis kolmen kokoinen dynaamisesti varattu struct-taulukko). Määrittele funktio, joka täyttää structit sekä funktio, joka tulostaa structien sisällön. Kutsu pääohjelmassa määrittelemiäsi funktioita.

Tunti 2

5.

Tee ohjelma, joka tallettaa tiedostoon taulukollisen kokonaislukuja. Käytä tiedostokuvaajiin perustuvaa tiedostojenkäsittelyä ja talleta data binäärimuodossa, ei asciina. Talletus siis tapahtuu tehtävässä 5 operaatiolla write ja tehtävässä 7 operaatiolla fwrite.

Syntyneen tiedoston sisältöä voi tutkia hexdump-komennolla.

6.

Tee ohjelma, joka lukee edellisessä tehtävässä tiedostoon talletetut luvut. Saat olettaa, että ohjelma tietää kuinka monta lukua tiedostossa on.

7.

Tee ohjelma, joka tallettaa tiedostoon taulukollisen kokonaislukuja. Käytä streameihin perustuvaa I/O:ta ja talleta data binäärimuodossa (eli fwrite:llä), ei asciina (eli älä käytä komentoa fputs tai fprintf).

8.

Tee käyttäen stream I/O:ta ohjelma, joka lukee edellisessä tehtävässä tiedostoon talletetut luvut. Saat olettaa, että ohjelma tietää kuinka monta lukua tiedostossa on.

Tehtävien 5-8 ohjelmien pitäisi olla yhteensopivia, eli esim. kohdan 5 ohjelmalla tehty tiedosto pitäisi olla luettavissa kohdan 8 ohjelmalla.


Tunti 3

* 9.

Tee ohjelma, joka saa komentoriviparametrina kokonaislukuja ja suorittaa luvuille optioiden avulla annetut laskutoimitukset. Ohjelma voi saada seuraavat optiot:
-s	tulostetaan parametrina annettujen lukujen summa
-t	tulostetaan parametrina annettujen lukujen tulo
-k	tulostetaan parametrina annettujen lukujen keskiarvo
Käytä komentorivioptioiden käsittelyyn getopt-funktiota. Ohjelmalle voi antaa monta optiota yhtä aikaa. Tällöin ohjelma suorittaa kaikkien optioidensa määrittelevät toimenpiteet. Huom: komennolla atoi pystyt muuttamaan merkkijonon kokonaisluvuksi.

10.

Tee ohjelma, joka saa komentoriviparametrina merkkijonon, jonka ohjelma printtaa ruudulle. Ohjelma voi lisäksi saada seuraavat merkkijonon tulostukseen vaikuttavat optiot:
-u	pienet kirjaimet tulostuvat vastaavana isona kirjaimena
-l	isot kirjaimet tulostuvat vastaavana pienenä kirjaimena
-n 	numerot korvautuvat merkillä #
Käytä komentorivioptioiden käsittelyyn getopt-funktiota. Kirjaston ctype.h funktioista on tehtävässä apua.

Ohjelman täytyy osata tulostaa merkkijono myös siinä tilanteessa, missä sille annetaan yhtä aikaa monta optiota.

Esim. ./ohjelma -lu KoeKoe tulostaa merkkijonon kOEkOE.

11.

Tee ohjelma, joa kysyy käyttäjätunnuksen ja salasanan. Salasanaa syöttäessä ruudulle tulostuu ainoastaan tähtiä. Jos syötetty salasana on sama kuin käyttäjätunnus väärin päin, tulostetaan "password accepted", muussa tapauksessa "login failed".

Eli jos annetaan käyttäjätunnus luuma, hyväksyttävä salasana on amuul.

Saat lisäpisteen, jos ohjelma toimii siten, että salasanaa syöttäessäsi taaksepäinnuolella (ascii-koodi 127) voit korjata syötettä. Myös printattujen tähtien täytyy vähentyä jos taaksepäinnuolta painetaan. Vihje: voit pyyhkiä kirjaimen "tulostamalla" deleten eli ascii-koodin 127 komennolla printf("%c",127); HUOM: tämä feature ei toimi jostain syystä jos järjestelmään on kirjauduttu OpenSSH:lla.


Tunti 4

12.

Tee ohjelma, joka tulostaa ruudulle ohjelman suoritushetken kellonajan ja päivämäärän. Ohjelman toimintaa ohjataan komentorivioptioilla. Optio -d tulostaa päivämäärän, -t kellonajan. Jos ohjelmalle ei anneta optioita tai annetaan optio -h, tulostaa ohjelma ruudulle käyttöohjeen.

* 13.

Ohjelma käyttää seuraavanlaista structia:
struct hlo{
  char nimi[80];
  int ika; 
};
Tee ohjelma, joka toimii kuten 4-2.c, mutta dynaamiseen taulukkoon talletetaan struct hlo -muotoisia tietueita.

14.

Täydennä edellistä ohjelmaa siten, että se lopuksi järjestää qsortia käyttäen taulukon sisällön erikseen sekä nimen, että iän perusteella. Taulukko siis järjestetään kahteen kertaan, ensin nimen perusteella ja tämä jälkeen iän perusteella. Saman nimisten ja ikäisten keskinäiseen järjestykseen ei tarvitse ottaa kantaa.

Viikot 5 ja 6

15.

Tee ohjelma, joka luo lapsen. Lapsi tulostaa pidinsä (eli prosessi-id:nsä) ja kuolee heti tämän jälkeen. Vanhempi odottaa lapsen lopetusta wait:illa, mutta ennen wait:ia vanhempi viivyttelee jonkin aikaa (esim. sleep:iä käyttäen).

Katso ps-komennolla miltä kuollut lapsi näyttää prosessilistalla.

Kuka on vanhemman vanhempi?

16.

Tee ohjelma, joka luo lapsen. Lapsi tulostaa heti oman ja vanhempansa pidin. Lapsi odottaa hiukan ja tänä aikana vanhempi kuolee. Vanhempi ei suorita wait- eikä waitpid-komentoa. Lapsi tulostaa jälleen oman ja vanhempansa pidin. Kuka on nyt lapsen vanhempi?

17.

Tee ohjelma, joka luo kaksi lasta. Ensimmäisenä luotu lapsi lopettaa käyttäen exit-statusta 1 (eli kutsuu lopuksi exit(1);) ja toinen lapsi käyttäen exit-statusta 2. Vanhempi odottaa ensin myöhemmin lapsen lopetuksen ja vasta tämän jälkeen ensimmäisenä luodun lapsen lopetuksen. Vanhempi tulostaa molempien lapsien exit-statuksen.

Huom: tässä tehtävässä vanhemman on käytettävä odottaessaan lapsen lopetusta waitpid:iä, sillä käytettäessä wait:ia ei voida vaikuttaa mitä lasta odotetaan.

18.

Tee ohjelma joka avaa ensin tilapäisen tiedoston ja tekee tämän jälkeen lapsen. Tiedosto on nyt sekä vanhemman että lapsen käytettävissä.

Lapsi kirjoittaa tiedostoon ohjelmalle komentorivillä annetun merkkijonon. Vanhempi odottaa lapsen lopettamista ja lukee merkkijonon tiedostosta. Huomaa, että vanhemman on siirrettävä tiedosto-osoitin tiedoston alkuun voidakseen lukea lapsen kirjoittaman merkkijonon, ks. 4-4.c

Muuta lasta siten, että lapsi juuri ennen lopetusta sulkee tiedoston. Voiko vanhempi vielä lukea tiedostoa?

* 19.

Tee yksinkertainen komentotulkki. Ohjelma toimii siten, että se kysyy ensin käyttäjältä merkkijonosyötettä. Tämän jälkeen ohjelma luo lapsiprosessin, joka suorittaa jotain exec-käskyn versiota käyttäen ohjelman jonka nimi on sama käyttäjän antama merkkijono. Vanhempi odottaa, lapsen lopetuksen ja kysyy käyttäjältä uuden syötteen jonka suorittamista varten luodaan taas uusi lapsi jne. Siinä vaiheessa kun kättäjä antaa syötteeksi "logout", ohjelma lopettaa.

Voit olettaa, käynnistettäville ohjelmille ei anneta parametrejä.

20. (advanced)

Laajenna edellistä ohjelmaa siten, että ohjelma osaa käynnistää ohjelmia myös siten, että niillä on komentoriviparametrejä, esim seuraavaan tyyliin.:
[luuma@telinux1 c]$ a.out
$ ps l Uluuma
F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  STAT TTY        TIME COMMAND
4 29188  2226  2224  15   0  4444 1328 wait4  S    pts/61     0:00 -bash
4 29188  2274  2271  15   0  4704 1424 wait4  S    pts/65     0:00 -bash
0 29188  6280  2226  15   0 11944 5972 schedu S    pts/61     0:01 emacs index.h
0 29188  9052  2274  16   0  1388  308 wait4  S    pts/65     0:00 a.out
0 29188  9054  9052  21   0  3296 1204 -      R    pts/65     0:00 ps l Uluuma
$
Joudut siis toteuttamaan käyttäjältä luetun syötteen pilkkomisen osiin ja ei ole ennalta tiedossa kuinka monta osia on. Tehtävä ei ole välttämättä helpoimmasta päästä. Tässä kannattaa ehkä hyödyntä funktion sscanf ominaisuuksia.

Jos kutsutaan: i = sscanf(buf, "%s", p) lukee sscanf merkkijonosta buf ensimmäisen yhtenäisen välilyöntiin loppuvan merkkijonon ja sijoittaa sen merkkijonomuuttujaan p. Jos sscanf onnistuu lukemisessa (eli buf:ssa on muutakin kun tyhjää, tabeja tai entereitä), palauttaa sscanf arvon 1.

Lisäys: c:stä löytyy funktio strtok, joka tekee merkkijonon pilkkomisen helpoksi, ks. man-sivu.


Tunti 7

21.

Tee ohjelma, jota ei saa tapettua signaaleilla SIGTERM tai SIGINT. Yritä saada ohjelma sellaiseksi, että sitä ei saa tapettua myöskään SIGKILL:illä. Onnistuuko?

* 22.

Prosessi luo lapsen. Aikuinen alkaa odottamaan signaalia lapselta. Lapsi viivyttelee hetken (esim. sleep:illä) ja signaloi vanhemmalle. Tämän jälkeen vanhempi lähettää signaalin, joka terminoi lapsen. Tuhoutuessaan lapsi tulostaa pid:insä.

23.

Lapsen kuollessa KJ lähettää äitiprosessille signaalin SIGCHLD. Tee prosessi joka luo lapsen. Prosessi käsittelee lapsen loppumisen (waitilla tai waitpid:llä) SIGCHLD:lle rekisteröidyssä signaalinkäsittelijässä.

Tunti 8

* 24.

Prosessi luo lapsen. Vanhempi lähettää lapselle putkea pitkin kaksi kokonaislukua. Lapsi vastaanottaa vanhemman lähettämät luvut ja lähettää niiden summan takaisin vanhemmalle. Vanhempi tulostaa putkesta lukemansa luvun. Käytä eri putkia vanhemmalta lapselle ja lapselta vanhemmalle tapahtuvassa kommunikoinnissa.

* 25.

Prosessi luo lapsen, joka suorittaa exec:in avulla komennon ls -l. Lapsen suorittaman komennon tulostus ohjataan putkeen ja vanhempi lukee ja tulostaa putkesta tulevan datan.

26.

Tutustu popen-komentoon ja tee popenin avulla sama asia mitä edellisessä tehtävässä. Huom: käyttäessäsi popenia ei ohjelmakoodin tarvitse forkata uutta prosessia vaan popen tekee forkkauksen automaattisesti.

27.

Prosessi luo kolme lasta joiden avulla se suorittaa saman kuin komentoriviltä annettu komento ps -A | grep bash | wc -l

Ole erityisen tarkkana, että suljet kaikissa prosesseissa tarpeettomat putken päät, muuten ohjelma ei toimi! Kannattaa ehkä aloittaa laittamalla kuntoon kahden ensimmäisen lapsen yhteys ja vasta tämän jälkeen liittää mukaan kolmas lapsi.

28. (advanced)

Prosessi luo kolme putkea ja kolme lasta. Lapset suorittavat looppia, jossa ne nukkuvat ensin 1-5 sekuntia (käytä satunnaisen pituisen odotuksen toteuttamiseen funktioita srand ja rand kuten C-kurssin esimerkissä esim3-1.c) ja sen jälkeen lähettävät vanhemmalle putkea pitkin kokonaisluvun (ensimmäinen lapsi luvun 1, toinen luvu 2, kolmas luvun 3), kukin lapsista käyttää omaa putkeaan.

Vanhempi odottaa yhtä aikaa kaikilta lapsilta tulevia lukuja ja tulostaa putkesta saapuvat luvut välittömästi. Vanhemman on lopetettava toiminta heti kun käyttäjä painaa enteriä. Lopettaessaan vanhempi ensin terminoi lapsensa.

Ohjelman toteuttaminen onnistuu käyttäen select-komentoa, katso esimerkki luentomateriaalista.


Tunti 9

HUOM: Muista että jaettua muistia käyttävät ohjelmat pitää kääntää käyttäen optiota -lrt.

* 29.

Ohjelma käyttää seuraavanlaista structia:
struct hlo{
  char nimi[80];
  int ika; 
};
Luodaan lapsi sekä jaettu muistialue jonka sekä lapsi että vanhempi näkevät. Vanhempi kysyy käyttäjältä tiedot yhteen struktiin ja tallettaa sen jaetulle muistialueelle. Lapsi lukee structin tiedot jaetusta muistista ja tulostaa ne.

Synkronoi vanhemman ja lapsen eteneminen semaforia käyttäen. Jaettua muistialuetta kannattaa käsitellä struct hlo -osoittimen avulla.

30.

Laajenna edellistä siten, että vanhempi kysyy ensin käyttäjältä kuinka monen structin tiedot annetaan. Tiedot talletetaan jaetulle muistialueelle ja lapsi tulostaa tiedot. Huom: lapsen on saatava jotenkin tieto siitä kuinka monta structia jaetulla muistialueella on. Asia voidaan ratkaista monella eri tavalla.

Voit olettaa, että kaikki syötettävä data mahtuu esim. 4096 tavun kokoiseen jaettuun muistiin.

31.

Prosessi luo lapsen. Lapsi ja vanhempi tulostavat vuorotellen ruudulle jonkin merkkijonon (esim. "olen lapsi", "olen äiti"). Kun molemmat ovat tulostaneet merkkijonon 10 kertaa, lopetetaan.

Kumpikaan prosessi ei siis saa tulostaa merkkijonoa kahteen kertaan peräkkäin! Hoida prosessien välinen synkronointi semaforien avulla, tarvitset todennäköisesti kahta semaforia.

32.

Prosessi luo kaksi lasta. Vanhempi 1 pyytää kokonaisluvun käyttäjältä. Lapsi 1 kasvattaa lukua viidellä ja lapsi 2 korottaa lapsen 1 päivittämän luvun potenssiin kaksi. Vanhempi tulostaa laskutoimituksen arvon ruudulle.

Käsiteltävä luku tallennetaan jaetulle muistialueelle. Hoida prosessien välinen synkronointi semaforien avulla.


Tunti 10

* 33.

Ohjelma saa parametrinaan merkkijonon (= tiedoston nimi) ja tulostaa tietoja parametrinsa nimisestä tiedostosta. Tulostuksen muotoa ohjataan seuraavilla argumenteillä:
-u	tulostetaan user id 
-g	tulostetaan group id 
-s	tulostetaan tiedoston koko
-l	tulostetaan linkkien lukumäärä
-t	tulostetaan tiedoston tyyppi (hakemisto, normaali tiedosto, joku muu)
Käytä getopt:ia argumenttien selvittämiseen. Ohjelman pitää toimia millä tahansa argumenttikombinaatiolla.

34.

Muuta esimerkkiä 10-3.c siten, että tiedostoista ja hakemistoista tulostetaan myös käyttöoikeudet, omistaja ja tiedoston luontipäivä samaan tapaan kun Linux ne tulostaa komennin ls -l yhteydessä, eli:
-rwxr-xr-x    1 luuma    luuma        3284 loka   28 13:10 index.html
drwxrwxr-x    2 luuma    luuma        4096 elo    18 15:27 kuvat
Eli ensimmäisenä merkkinä d jos kyseessä hakemisto, muuten -, loput merkit (rwx) oikeuksien mukaan. Käyttäjän ja ryhmän paikalla riittää uid:n ja gid:n tulostaminen.

35. (advanced)

Ohjelma saa komentoriviparametrina merkkijonon. Ohjelma etsii onko työhakemistossa tai missään työhakemiston alihakemistossa (tai näiden ali, aliali, ... hakemistoissa) olemassa tiedostoa, jonka nimi on sama kun parametrina saatu merkkijono. Ohjelma on ehkä helpoin toteuttaa käyttäen rekursiota.

Tunti 11

* 36.

Tee ohjelma joka koostuu oletussäikeen lisäksi kymmenestä säikeestä. Jokainen säie suorittaa samaa funktiota. Säikeet saavat parametrina kokonaisluvun. Kukin säikeistä tulostaa ruudulle saamansa parametrin 100 kertaa.

Muista, että tarvitset säieohjelmia kääntäessäsi option -lpthread

* 37.

Tee ohjelma joka kysyy käyttäjältä positiivista kokonaislukua. Saatuaan luvun (=x), ohjelma käynnistää kaksi säiettä josta toinen laskee luvun kertoman (1*2*...*x) ja toinen summan 1+2+...+x. Säikeet palauttavat tuloksen ohjelman oletussäikeelle (eli mainille) joka tulostaa vastaukset ruudulle.

HUOM: on ilmennyt, että edunix.metropolia.fi-koneessa säikeiden on palautettava arvo main:ille muistiosoitteen avulla, kuten esimerkissä 11-5.c

38.

Ohjelmalla on globaali taulukko
float neliojuuret[100];
Taulukko on tarkoitus täyttää siten, että taulukon indeksiin i talletetaan luvun i neliöjuuri. Toteuta ohjelma siten, että jokaisen taulukkoon tulevan luvun laskee ja tallettaa globaaliin taulukkoon oma säikeensä. Kun kaikki neliöjuuret on laskettu, oletussäie tulostaa taulukon sisällön.

Tunti 12

* 39.

Ohjelmassa on oletussäikeen lisäksi kaksi säiettä. Säikeet käyttävät 10000 paikkaista globaalia taulukkoa. Säie 1 on loopissa, jossa se nukuttuaan satunnaisen ajan arpoo taulukolle uuden sisällön. Säie 2 suorittaa myös looppia jossa se nukkuu satunnaisen ajan. Herättyään säie 2 tulostaa taulukossa olevien lukujen keskiarvon.

Suojaa taulukon käsittely mutexilla.

Ohjelman suoritus loppuu siten, että suoritettuaan loopin 10 kertaa säie 1 kirjoittaa taulukon jokaisen paikkaan arvon -9999 ja lopettaa. Kun säie 2 huomaa tilanteen, myös se lopettaa.

40.

Laajenna edellistä tehtävää siten, että lukijoita on viisi ja mutexin sijasta käytetään lukija/kirjoittajalukkoa.

Muista, että käännös täytyy tehdä seuraavasti:

  gcc koodi.c -lpthread -D_XOPEN_SOURCE=600

41.

Ohjelmassa on oletussäikeen lisäksi kolme säiettä, jotka käyttävät jaettua muuttujaa x, jonka alkuarvo on 0. Säie 1 ja 2 ovat ikuisessa loopissa. Molemmat odottavat satunnaisen jonka jälkeen ne muuttavat x:n arvoa. Säie 1 kasvattaa x:n yhdellä ja säie 2 vähentää x:n arvoa yhdellä. Säie 3 seuraa x:n arvoa ja tulostaa jotain ruudulle jos x:n arvo ylittää tai alittaa välin -2...2, samalla säie 3 nollaa x:n arvon. Kun nollaus on tapahtunut 5 kertaa, säie 3 lopettaa. Tämän jälkeen ohjelman oletussäie terminoi säikeet 1 ja 2.

Oikeaoppisesti toteutettuna säie 3 odottaa x:n arvon muutosta ehtomuuttujan takana.

42. (advanced)

Muuta ohjelmaa 12-5.c seuraavasti:
  • Sekä kirjoittaja- että lukijasäikeiden lukumäärä on 5.
  • Käytössä globaali muuttuja, alkuarvona 0.
  • Kukin kirjoittajasäie toimii loopissa, jossa se:
  • odottaa satunnaisen ajan,
  • lukee globaalin muuttujan arvon ja kasvattaa sitä yhdellä
  • kirjoittaa lukemansa globaalin muuttujan arvon jonoon
  • Lukijoiden ja kirjoittajien kommunikointiin käyttämään jonoon talletetaan korkeintaan 3 alkiota kerrallaan. Eli jos jonossa on jo kolme alkiota, on kirjoittajan odotettava ennen kuin jonoon voi lisätä dataa.