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: