Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Copyright  Hannu Laine Reaaliaikaohjelmointi Osa 9 Hannu Laine Putket Fifot.

Samankaltaiset esitykset


Esitys aiheesta: "Copyright  Hannu Laine Reaaliaikaohjelmointi Osa 9 Hannu Laine Putket Fifot."— Esityksen transkriptio:

1 Copyright  Hannu Laine Reaaliaikaohjelmointi Osa 9 Hannu Laine Putket Fifot

2 HL1 Prosessien kommunikointi putkella IPC on lyhenne termille Inter Process Communication. Tiedostoa voidaan aina käyttää prosessien väliseen kommunikaatioon, mutta silloin synkronointi on ongelma. Seuraavassa esimerkki tiedoston käytöstä prosessien väliseen kommunikointiin: Prosessi A kirjoittaa laskurin arvoa tiedostoon ja prosessi B lukee sitä. Pidetään vaatimuksena, että prosessi B saa luettua jokaisen prosessin A tuottaman laskurin arvon ja lisäksi se lukee jokaisen arvon vain kerran. Tarkastellaan vaatimusten täyttämisen ongelmia. Prosessi A Prosessi B Levytiedosto n Putki on kätevä ratkaisu yllämainittuun ongelmaan.

3 HL2 Prosessien kommunikointi putkella Putki on vanhin ja varmin tapa prosessien väliseen kommunikointiin. Putkessa synkronointi on hoidettu sisäisesti. Putken rajoitukset: 1. Putkessa tieto siirtyy vain yhteen suuntaan. Suunta voidaan valita. 2. Putkea voidaan käyttää vain sellaisten prosessien välillä, joilla on yhteinen vanhempi. Näistä rajoituksista päästään eroon nimetyillä putkilla (FIFO) ja soketeilla. Useimmiten putkea käytetään välittämään tietoa äitiprosessin ja lapsiprosessin välillä.

4 HL3 Funktiot putken käytössä Funktio pipe luo putken. int pipe(int fd[ ]); (tai int pipe(int *fd); ) palauttaa 0, jos OK, ja -1 virheessä. Funktiolle välitetään kahden filedeskriptorin taulukko, jonne funktio asettaa kaksi avattua filedeskriptoria. Taulukon alkiossa 0, on filedeskriptori lukemista varten ja alkiossa 1 on filedeskriptori kirjoittamista varten. Kaikki mitä kirjoitetaan filedeskriptoriin fd[1] voidaan lukea filedeskriptorista fd[0]. Jos funktiolla fstat kysytään tiedoston tyyppiä (struct stat tietueen kenttää st_mode makrolla S_ISFIFO testattaessa saadaan true), niin saadaan FIFO. Normaalisti prosessi A sulkee deskriptorin fd[0] ja prosessi B deskriptorin fd[1] tai päinvastoin halutusta siirtosuunnasta riippuen. Erilaisia tilanteita: Putkesta luetaan, kun sen kirjoituspää on suljettu => read palaa palautusarvolla 0 (end of file). Putkeen kirjoitetaan, kun sen lukupää on suljettu => saadaan signaali SIGPIPE. Jos signaalin- käsittelijästä palataan (return), niin funktio write palaa ja errno on EPIPE

5 HL4 Putken luonti Seuraava lyhyt esimerkki valaisee putken luomisen äitiprosessista ja lapsiprosessiin. int fd_arr[2];... pipe(fd_arr); // Solid things exist after this pid = fork(); // Also dotted things exist after this if (pid == 0) { close (fd_arr[1]); // see * in the figure read(fd_arr[0], &chr, 1);... } if (pid > 0) { close (fd_arr[0]) ; // see ** in the figure write(fd_arr[1], “A!, 1);... } * Prosessi A Prosessi B **

6 HL5 Lisää putkista Vakio PIPE_BUF kertoo putken koon (esim. 4096). Myös putkea edustavat filedeskriptorit voidaan laittaa blokkaavaan tai ei-blokkaavaan tilaan. Blokkaavassa tilassa lukeminen blokkaa, kun putkessa ei ole luettavaa ja kirjoittaminen blokkaa, kun putkeen ei mahdu sitä määrää uutta tavaraa kun yritetään kirjoittaa. Putken synkronoinnin tarkastelua: Lähettäjä on lukijaa hitaampi => Lukija odottaa blokkaantuneena, että putkeen tulee tavaraa (vakiintuneessa tilassa). Lukija on kirjoittajaa hitaampi => Kirjoittaja odottaa blokkaantuneena, että putkeen tulee tilaa kirjoitusta varten (vakiintuneessa tilassa). Huomautus. On taattua, että kirjoitettaessa putkeen tietomäärä, jonka on korkeintaan PIPE_BUF tavua, kirjoitus on atominen. Yksinkertainen putkiesimerkki seuraavalla sivulla.

7 HL6 Esimerkkiohjelma putkesta // This is very simple pipe example. Parent writes to the pipe // Child reads from the pipe #include int main(void) { pid_t pid; int fd_arr[2], i, n; char chr; //This part is done only by parent pipe(fd_arr); pid = fork(); if (pid == 0) { // this is child close (fd_arr[1]); // child closes it's write end of the pipe // reading from the pipe for( i = 0 ; i < 10 ; i++) { read(fd_arr[0], &chr, 1); printf("%c\n", chr); } close(fd_arr[0]); exit (0); } // parent continues from here close (fd_arr[0]); // parent closes it's read end of the pipe // writing to the pipe for( i = 0 ; i < 10 ; i++) { chr = 'a' + i; write (fd_arr[1], &chr, 1); sleep(1); } close (fd_arr[1]); }

8 HL7 Putket ja filedeskriptorit Filedeskriptorit ovat prosessikohtaisia ja ne ovat kernelin prosessitaulussa. Avattaessa tiedostoa, luodaan myös uusi alkio kernelin tiedostotaulukkoon. Funktiolla dup voidaan kahdentaa filedeskriptori. Silloin molemmat deskriptorit osoittavat samaan alkioon tiedostotaulukossa. Alkio tiedostotaulukosta poistetaan vasta, kun siihen ei ole kytketty yhtään deskriptoria (close). Lapsiprosessin luonnissa lapsiprosessille tulee siis omat deskriptorit, joiden arvot omat samat ja jotka osoittavat samoihin tiedostotaulukon alkioihin (vrt dup). Exec-funktiot vaihtavat ohjelmaa prosessin sisällä mutta itse prosessi säilyy. Prosessitaulussa siis pysyy lapsen avoimet deskriptorit myös tässä tapauksessa, mikäli filedeskriptor flagi close-on- exec ei ole päällä. Kuinka exec-tapauksessa saadaan tieto filedeskriptoreista uuden ohjelman käyttöön. (kts. seuraava kalvo)

9 HL8 Filedeskriptorin välitys toiselle ohjelmalle Tapa 1. Välitetään parametrina. Filedeskriptori voidaan lähettää ohjelman parametrina: execl("./pipe_end.exe", "pipe_end.exe", fd_string, NULL); Tapa 2. Kytketään deskriptori johonkin ”vakiodeskriptoriin” (usein standardi inputiin tai standardi outputiin). Esimerkki. Lähdetään siitä, että lapsen halutaan kirjoittavan putkeen. Kirjoitetaan lapsen koodi siten, että lapsi kirjoittaa standardioutputiin. Silloin koodi ohjelmassa, joka forkkaa lapsen on seuraava: // pipe is opened // fork is done //this is child close (fd_arr[0] ); if (fd_arr[1] != STDOUT_FILENO) { dup2( fd_arr [ 1 ], STDOUT_FILENO); close(fd_arr [ 1 ] ); } execl("./pipe_end.exe", "pipe_end.exe", NULL);

10 HL9 Deskriptorien aukipysyminen execissä Kuten edellä sanottiin, lapsella auki olevat filedeskriptorit pysyvät oletusarvoisesti auki vaikka lapsiprosessi käynnistää uuden ohjelman execillä. Tämä voidaan estää filedeskriptor flagilla close-on-exec. Tämä flagi on siis filedeskriptor flagi, joka on eri asia kuin file flagit. Filedeskriptor flagi on tallennettuna prosessin filedeskriptor taulussa kun taas fileflagit ovat tiedostotaulukossa (kts. kuva 3.4) Tämä flagi voidaan asettaa seuraavasti: nt fdflags; fdflags = fcntl(fd, F_GETFD, 0); fdflags | = FD_CLOEXEC; fcntl(fd, F_SETFD, fdflags); Vielä pikku lisäesimerkki filedeskriptoreista: int fd1, fd2; fd1 = open(”data.dat”, O_RDONLY); fd2 = open(”data.dat”, O_WRONLY); Tässä syntyy kaksi entryä prosessin filedeskriptoritauluun ja kaksi entryä filetaulukkoon, jolloin molemmilla on mm myös file flagit ja offset.

11 HL10 Putken käyttötapoja On useampia suunnittelumalleja (design patterns), joissa käytetään putkea apuvälineenä. Tässä niistä muutamia: Äidillä yhteys lapsiin Erilaisia liukuhihnaratkaisuja (pipeline) Barrier (este) Putki synkronointivälineenä (tiedonsiirrolla ei merkitystä). Komentotulkissa tapahtuva putkitus Periaateratkaisu tilanteesta, jossa äitiprosessilla on yhteys lapsiin, nähdään alla. Yhteys voisi olla äidistä lapsiin tai lapsista äitiin tai myös molempiin suuntiin, jos äidin ja lapsen välillä olisi kaksi putkea. Äitiprosessi Lapsiprosessit Pari erilaista liukuhihnaratkaisua on alla: Äitiprosessi Lapsiprosessit

12 HL11 Barrier Näin äitiprosessi voi taata, että kaikki lapset ovat olemassa ennen, kuin ne saavat aloittaa työskentelyn. //This program demonstrates a barrier #define N 100 int main(void) { pid_t pid; int pipe_fd[2]; int i; char chr; pipe(pipe_fd); for (i = 0; i < N ; i++) { pid = fork(); if (pid == 0) { //this is child read(pipe_fd[0], &chr, 1); //each child waits // here until all are ready printf("Child is running\n"); sleep(i+1); exit(0); } else printf("Child is created\n"); } for (i = 0 ; i < N ; i++) write(pipe_fd[1], "A", 1); }

13 HL12 PIPE as a synchronization tool synchronize.h void initialize_sync(void); void wait_child(); void wait_parent(); void tell_parent_to_continue(); void tell_child_to_continue(); synchronize.c static int pipe_from_child[2], pipe_from_parent[2]; static char c = ’c’, p = ’p’; void initialize_sync(void) { pipe(pipe_from_child); pipe(pipe_from_parent); } void wait_child() { char chr; read(pipe_from_child[0], &chr, 1); } void wait_parent() { char chr; read(pipe_from_parent[0], &chr, 1); } void tell_parent_to_continue() { write(pipe_from_child[1], &c, 1); } void tell_child_to_continue() { write(pipe_from_parent[1], &p, 1); }

14 HL13 Lisää putken erilaisia käyttötapoja Putkea voidaan käyttää erilaisissa yhteyksissä kuten edellä on nähty. Palaamme vielä kahteen tapaukseen, jotka ovat oikeastaan edellisten erikoistapauksia. Voidaan toteuttaa rinnakkain toimivat prosessit (ns. rinnakkaisprosessit). Voidaan toteuttaa liukuhihnajärjestelmä kirjastofunktioilla, jossa yksi prosessi tekee tiedolle alkukäsittelyn, toinen prosessi jatkokäsittelyn ja kolmas loppukäsittelyn (tai vaiheita voi olla enemmän). Rinnakkaisprosessien periaate on kuvattua alla: Prosessi 1 käyttää hyväksi prosessin 2 palvelua tiedon jalostamiseen. Tarvitaan yksi putki käsiteltävän tiedon lähettämiseen ja toinen putki tulosten vastaanottamiseen. Prosessi 1Prosessi 2

15 HL14 Liukuhihna kirjastofunktioilla Liukuhihnajärjestelmän periaate on kuvattu alla. Prosessi 1 tekee alkukäsittelyn ja lähettää tiedon prosessille 2 jatkokäsiteltäväksi. Prosessi 2 lähettää jatkokäsitellyn tiedon prosessille 3 loppukäsittelyyn. Prosessi 1Prosessi 2Prosessi 3 Standardikirjaston funktiot popen ja pclose tukevat tällaisen järjestelmän toteuttamista. Silloin eri pro- sessien ohjelmat näyttävät seuraavilta (eräs tapa). Prosessi 1 (ohjelma alkukas.c) int main(void) { Tdata data; FILE *jatkokasittelyyn; jatkokäsittelyyn = popen(”jatkokas.exe”, ”w”); while (...) { //hankitaan data ja alkukäsitellään se fwrite(&data, sizeof(data), 1, jatkokasittelyyn);.... } pclose(jatkokasittelyyn); }

16 HL15 Liukuhihna kirjastofunktioilla (jatk) Prosessi 2 (ohjelma jatkokas.c) int main(void) { Tdata_in data_in; Tdata_out data: FILE *loppukasittelyyn; loppukäsittelyyn = popen(”loppukas.exe”, ”w”); while (...) { //luetaan data alkukäsittelijältä fread(&data_in, sizeof(data_in), 1, stdin); //jatkokäsitellään data... //lähetetään loppukäsittelyyn fwrite(&data, sizeof(data),1, loppukasittelyyn);.... } pclose(loppukasittelyyn); } Prosessi 3 (ohjelma loppukas.c) int main(void) { Tdata_in data_in; while (...) { //luetaan data jatkokäsittelijältä fread(&data_in, sizeof(data_in), 1, stdin); //loppukäsitellään data... // }

17 HL16 Funktiot popen ja pclose Edellä annettiin esimerkki funktioiden popen ja pclose käytöstä. Käytetään esimerkkinä funktion kutsua jatkokäsittelyyn = popen(”jatkokas.exe”, ”w”); Funktio popen tekee silloin seuraavat asiat: Avaa putken (pipe). Forkkaa lapsiprosessin. Lisäksi vanhemmassa: Sulkee putken lukupään vanhemmassa. Lisäksi lapsessa Sulkee putken kirjoituspään lapsessa. Kahdentaa putken lukupään deskriptoriin STDIN_FILENO lapsessa. Sulkee alkuperäisen putken lukudeskriptorin. Tekee execin käynnistäen ohjelman jatkokas.exe. Funktio pclose tekee seuraavat asiat Odottaa lapsiprosessin päättymistä. Sulkee putkeen liittyvän vuon.

18 HL17 FIFO FIFOa sanotaan myös nimetyksi putkeksi. Sen etuna (yksinkertaiseen putkeen) nähden on, että toisistaan riippumattomat prosessit voivat kommunikoida sen avulla. Tämä johtuu siitä, että FIFOa edustaa tiedostonimi hakemistorakenteessa, vaikka FIFO ei olekaan tavallinen tiedosto (regular file, vaan FIFO). FIFOon voi olla useita kirjoittajia ja sitä voidaan käyttää client-server tyyppisen systeemin toteutuksessa. Putki luodan funktiolla int mkfifo(const char *pathname, mode_t mode); Palauttaa 0, jos OK ja -1 jos ei OK. Parametri mode on samanlainen kuin tiedostojen avauksessa oleva mode parametri (määrää siis oikeudet). Tämän jälkeen FIFO avataan lukua tai kirjoitusta varten kuten tavalliset tiedostot funktioilla open. Samoin FIFOon kirjoitetaan ja sieltä luetaan funktioilla write ja read. Myös avaamiskutsut blokkaavat, jos toinen pää ei ole avattu ja avauskutsussa ei ole O_NONBLOCK flagia.

19 HL18 Lisää FIFOsta Jos FIFO avataan read-only-modessa, avaus blokkaa jos toinen pää ei ole auki kirjoitusta varten. Jos FIFO avataan write-only-modessa, avaus blokkaa jos toinen pää ei ole auki lukemista varten. Molemmat lauseet ovat voimassa siis jos avaus-flagiä O_NONBLOCK ei ole asetettu. Prosessi voi avata FIFOn kirjoitusta ja lukua varten (eli siis avata molemmat päät). Silloin avauskutsu ei voi blokata. Synkronointi FIFOssa toimii täysin samalla tavalla kuin putkessa eli read-funktio blokkaa, jos FIFO on tyhjä ja write funktio blokkaa jos FIFO on täynnä. Jos kirjoitetaan putkeen, joka ei ole auki lukua varten generoituu signaali SIGPIPE. Jos tälle signaalille on vain oletuskäsittelijä, niin prosessi päättyy. Jos kirjoitetaan käsittelijä, joka palaa, niin write funktio palaa ja virhekoodina on EPIPE. Kun viimeinen kirjoittaja sulkee putken, niin lukijalle generoituu EOF, kun se lukee tyhjäksi luetusta putkesta (read palauttaa nollan). Putkeen kirjoitus on atomista, kunhan sinne ei kirjoiteta enemmän kuin vakio PIPE_BUF ilmaisee. Putkesta lukeminen ei ole atomista. Lukufunktio, voi siis palata, vaikka kaikkia pyydettyjä tavuja ei ole luettu.

20 HL19 FIFO ei-blokkaavassa moodissa Synkronointi FIFOssa toimii “luontaisesti” ja “huomaamatta”, kun FIFO (tai putki) on blokkaavassa moodissa. Myös FIFO voidaan laittaa kuitenkin ei- blokkaavaan moodiin. Jos avauskutsussa on flagi O_NONBLOCK, niin avaaminen read-only tilaan palaa välittömästi ilman virhettä vaikka toista päätä ei olisikaan avattu kirjoitusta varten. Jos flagi O_NONBLOCK on päällä avauksessa write-only tilaan, open palaa välittömästi tuloksena virhe ENXIO, jos toista päätä ei ole avattu lukemista varten. Samalla tavalla, jos FIFO on ei-blokkaavassa moodissa, read-funktio palaa välittömästi, vaikka FIFO olisi tyhjä ja write funktio palaa välittömästi vaikka FIFO olisi täynnä. Molemmissa tapaukissa errno olisi EAGAIN.

21 HL20 Client/Server FIFOilla FIFOon voi olla monta kirjoittajaa ja yksi lukija. Silloin clientit voivat ottaa yhteyttä serveriin. Server ei kuitenkaan voi lähettää vastauksia clienteille yhtä FIFOa käyttäen. Eräs menetelmä on, että clientit lähettävät prosessin ID:n serverille ja se luo kullekin clientille oman putken vastauksen lähettämistä varten jonkin nimeämiskäytännön mukaan, esimerkiksi siten, että putken nimeksi annetaan server_name.xxxx missä xxxx on asiakkaan prosessitunnus.


Lataa ppt "Copyright  Hannu Laine Reaaliaikaohjelmointi Osa 9 Hannu Laine Putket Fifot."

Samankaltaiset esitykset


Iklan oleh Google