Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Terminaaliasetukset Sivut 1 – 7 käsittelevät terminaali i/o:ta. Niitä ei käsitellä tunnilla. Asioiden tunteminen voi olla hyödyllistä, sillä niitä voidaan.

Samankaltaiset esitykset


Esitys aiheesta: "Terminaaliasetukset Sivut 1 – 7 käsittelevät terminaali i/o:ta. Niitä ei käsitellä tunnilla. Asioiden tunteminen voi olla hyödyllistä, sillä niitä voidaan."— Esityksen transkriptio:

0 Reaaliaikaohjelmointi Osa 8
Terminaaliasetukset Asynkroninen i/o Multipleksattu i/o Hannu Laine Copyright  Hannu Laine

1 Terminaaliasetukset Sivut 1 – 7 käsittelevät terminaali i/o:ta. Niitä ei käsitellä tunnilla. Asioiden tunteminen voi olla hyödyllistä, sillä niitä voidaan soveltaa esimerkiksi sarjaporteissa. On kaksi käyttömoodia: 1. Kanoninen moodi (input prosessoidaan riveinä). 2. Ei-kanoninen moodi (merkeistä ei rakenneta rivejä). Huom. Tätä ei saa sekoittaa i/o-kirjaston tekemään puskurointiin. Nyt puhutaan kernelin i/o-toiminnoista. Kanonisessa moodissa systeemikutsu read palaa vain rivinvaihdosta (tai mahdollisesti signaalista). Kanoninen moodi on oletus. Posix-määrittelee 11 erikoismerkkiä, jotka käsitellään erikoisella tavalla kanonisessa moodissa (esimerkiksi CR, EOF, ERASE, INTR, KILL (line erase char), START (XON), STOP (XOFF). Input jonon (type ahead buffer) koko (MAX_INPUT). Canonisen rivin max koko (MAX_CANON). Input jono voidaan tyhjentää funktiolla tcflush (esimerkki myöhemmin). HL

2 Tietue termios Asetusten kontrollointiin tarvitaan tietuetta termios:
struct termios { tcflag_t c_iflag; //input flags tcflag_t c_oflag; //output flags tcflag_t c_cflag; //control flags tcflag_t c_lflag; //local flags cc_t c_cc[NCCS]; //control characters }; Tila saadaan selville ja asetettua funktioilla <termios.h> int tcgetattr(int filedes, struct termios *pterm_struct); int tcsetattr(int filedes, int opt, const struct termios *pterm_struct); Vaihtoehdot parametrille opt TCSANOW Muutokset voimaan heti. TCSADRAIN Muutokset voimaan, kun output mennyt perille. TCSAFLUSH Kuten edellä ja lisäksi lukematon input-data hylätään. Funktio tcgetattr laittaa nykyiset terminaaliasetukset tietueeseen struct termios. Funktio tcsetattr muuttaa terminaaliasetukset tietueen struct termios mukaisiksi. Funktio tcsetattr palauttaa 0 (OK), jos se sai asetettua edes jonkin vaadittavista flageistä! HL

3 Tietueen termios flagien käsittely
Flagi voi olla bitti tai bittikenttä. Flagejä käsitellään maskeilla ja vakioilla, jotka edustavat arvoja. Maskeilla ja arvoilla on nimet. Esimerkiksi merkin bittimäärä esitetään bittikentällä, jolle on maski CSIZE ja johon voidaan asettaa arvot CS5, CS6, CS7 tai CS8. Esimerkki 1. Vaihdetaan merkin bittimääräksi 8. struct termios term; tcgetattr(STDIN_FILENO, &term); term.c_cflag = term.c_cflag & ~CSIZE; term.c_cflag = term.c_cflag | CS8; tcsetattr(STDIN_FILENO, TCSANOW, &term); Esimerkki 2. Poistetaan echo ja kanonisuus struct termios settings, old_settings; tcgetattr(STDIN_FILENO, &settings); old_settings = settings; settings.c_lflag= settings.c_lflag & ~(ICANON|ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &settings); ... tcsetattr(STDIN_FILENO, TCSANOW, &old_settings); Tietueen struct termios kenttiin liittyvät flagit on esitetty kirjan (Stevens & Rago) kuvissa 18.3 – 18.6 (kopio jaettu). HL

4 Lisää ”terminaalifunktioita”
Nopeuden asetukset speed_t cfgetispeed(const struct termios *tp); speed_t cfgetospeed(const struct termios *tp); int cfsetispeed(struct termios *tp, speed_t speed); int cfsetospeed (struct termios *tp, speed_t speed); Myös nopeudet ilmaistaan vakioilla: esim B2400, B9600, B19200, B38400 Kaikki tässä ja aikaisemmin esitetyt toimet voidaan tehdä myös komennolla stty. Terminaalin asetukset saadaan näyttöön komennolla stty –a. Sarjaportin S0 asetukset vastaavasti komennolla stty –F/dev/ttyS0 –a. Echo ja kanonisuus saadaan pois komennolla stty –echo –icanon. Line control functions <termios.h> int tcdrain(int filedes); Odottaa kunnes kaikki output lähetetty. int tcflow(int filedes, int action); Käytetään XON/XOFF-vuonohjaukseen. int tcflush(int filedes, int queue); Tällä voidaan tyhjentää input jono ja output jono. Parametri queue voi olla TCIFLUSH (Input queue) TCOFLUSH (Output queue) TCIOFLUSH (Molemmat jonot tyhjennetään). HL

5 Muita funktioita Prosessin kontrolliterminaalin nimen kysyminen:
<stdio.h> char *ctermid(char *ptr); Esim 1. char control_terminal_id[L_ctermid]; ctermid(control_terminal_id); printf(”The name of controlling terminal is %s\n”, control_terminal_id); Esim 2. ctermid(NULL)); Minkä tahansa terminaalin nimen kysyminen, joka on avattu tiettyyn filedescriptoriin: <unistd.h> char *ttyname(int filedes); Funktio palauttaa osoittimen nimeen tai NULL- osoittimen, jos virhe tai, jos filedescriptoriin liittyvän tiedoston tyyppi ei ole ”Character special file”. Esim 3. int fd; ... printf(”Term dev name is %s\n”, ttyname(fd)); HL

6 Timeout ei-kanonisessa moodissa
Lukufunktio read palaa kun 1. Määrätty määrä tavuja luettu. 2. Asetettu timeout on kulunut. Timeout ilmaistaan kahdella arvolla termios tietueen c_cc taulukossa. Ensimmäistä arvoa kutsutaan MIN ja toista TIME. MIN on taulukossa paikassa VMIN ja TIME paikassa VTIME. Ajan yksikkö on 1/10 sekuntia. Tapaus 1. MIN > 0 ja TIME > 0 Jää odottamaan ensimmäistä merkkiä. Ensimmäisen merkin tultua käynnistyy timeri. Palataan, kun kaikki luettu tai timeri laukeaa. Tapaus 2. MIN > 0 ja TIME = 0 Palataan, kun kaikki luettu. Tapaus 3. MIN == 0 ja TIME > 0 Timeri käynnistyy heti. Palataan, kun yksi merkki luettu tai timeri laukeaa. read palauttaa 0, jos ei saatu merkkiä. Tapaus 4. MIN == 0 ja TIME == 0 Palaa aina heti. Merkkejä saadaan niin paljon kuin jonossa on. Vertaa joissakin ympäristöissä oleva ”kbhit”-funktio. HL

7 Esimerkki: polling keyboard
#include <stdio.h> #include <unistd.h> #include <termios.h> #include <stdlib.h> int main (void) { char chr; int result; struct termios term; tcgetattr(STDIN_FILENO, &term); term.c_lflag &= ~ICANON; term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 1; // tai 0 toimii myös if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)< 0) fprintf(stderr, ”We failed to change terminal settings\n"); //polling the keyboard while ( 2 < 3) { result =read(STDIN_FILENO, &chr, 1); if (result ==1) { //do something with input printf("Character %c was read\n", chr); } //do something ”more important” printf("We are working all the time\n"); HL

8 Ongelma käsiteltäessä useita laitteita
Palaamme taas reaaliaikajärjestelmän perusongelmaan eli siihen, että sovelluksen on annettava reaaliaikaisesti vastaus useisiin inputeihin. Jatkamme myös samalla esimerkillä eli yksinkertaisella chat- sovelluksella. Ongelmana on, että pitää lukea koko ajan kahdelta (tai useammalta) laitteelta. Tällainen on tilanne juuri kaksisuuntaisessa kommunikaatio-ohjelmassa. Luetaan näppäimistöä ja tietoliikennelinjaa. Kummankaan lukua ei voi blokata (esimerkki alla). Esitämme toimimattoman ratkaisun tässä kertauksena: char chr_from_kb, chr_from_line; int fd_kb, fd_line, fd_display; ... while (...) { read(fd_kb, &chr_from_kb, 1); //ei saa blokata write(fd_line, &chr_from_kb, 1); read(fd_line, &chr_from_line, 1);// ei saa blokata write(fd_display, &chr_from_line, 1); } Olemme jo nähneet kuinka ongelma voidaan ratkaista kahdella tavalla 1. Jaetaan työ kahteen erilliseen prosessiin, jolloin voidaan käyttää molemmissa blokkaavaa lukua. 2. Pysytään yhdessä prosessissa, mutta luetaan molempia laitteita ei-blokkaavasti.. HL

9 Uusia ratkaisuja Muita ratkaisuja ovat
3. Käytetään i/o-multipleksausta. 4. Käytetään asynkronista I/O:ta eli pyydetään käyttöjärjestelmältä signaalia, kun merkki on luettavissa. 5. Käytetään säikeitä. Tässä osassa näemme, kuinka käytetään multipleksattua i/o:ta ja asynkronista i/o:ta. Säikeisiin palataan myöhemmin. Multipleksatun i/o:n perusidea on, että koska yksittäistä deskriptoria ei voida lukea (tai kirjoittaa blokaten), jäädään blokaten odottamaan yhtä aikaa kaikkia niitä deskriptoreja, joiden inputtiin pitää vastata välittömästi ja niitä deskriptoreja, joihin pitää kirjoittaa välittömästi, kun se on mahdollista. Kun yksikin deskriptoreista on valmis antamaan välittömän inputin tai ottamaan vastaan välittömästi ouputin, odotuksesta palataan ja kyseiset operaatiot tehdään. Seuraavalla sivulla esiteltävä funktio select toimii tällä tavalla. HL

10 Multipleksattu i/o Funktiolla select voidaan odottaa useita filedescriptoreita yhtä aikaa (esim fd_kb ja fd_line edellä). Kun joku odotettavista tulee valmiiksi (niin että sieltä voidaan lukea tai sinne kirjoittaa) select funktio palaa. Funktiolle voidaan asettaa myös timeout, jonka kuluttua se palaa joka tapauksessa. Funktio select <sys/types.h> <sys/time.h> <unistd.h> int select(int maxfdplus1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timevalptr); Palauttaa valmiina olevien deskriptorien määrän tai 0, jos timeout -1, jos error. Parametrilla maxfdplus1 ilmaistaan suurimman odotettavan filedescriptorin numero lisättynä yhdellä. Seuraavalla parametrilla (readfds) ilmaistaan ne filedescriptorit, joiden lukuvalmiutta odotetaan. Parametrilla writefds ilmaistaan ne filedescriptorit, joiden kirjoitusvalmiutta odotetaan. Parametrilla exceptfds ilmaistaan ne filedescriptorit, joiden poikkeustilanteesta halutaan tieto. HL

11 Multipleksattu i/o (jatk)
Viimeisenä funktion select parametrina on osoitin tietueeseen struct timeval, jonka määrittely on: struct timeval { long tv_sec; //seconds long tv_usec; //microseconds }; Tapaus 1. timevalptr == NULL select odottaa vaikka ikuisesti. Tapaus 2. sekunnit ja mikrosekunnit ovat 0 select ei odota ollenkaan (polling). Tapaus 3. Seconds tai microsecons  0 Odottaa annetun ajan. Funktio select odottaa kaikkia kolmessa parametrissa annettuja filedeskriptoreita, kunnes jostakin voidaan lukea, kirjoittaa tai jossakin on poikkeustilanne. Silloin funktio palaa. Valmiiden deskriptorien määrä on palautusarvona. Parametreissa readfds, writefds on päällä vain ne bitit, joita vastaavat deskriptorit ovat valmiita lukemiseen tai kirjoittamiseen. Funktio palaa myös, jos parametrin ilmaisema timeout on kulunut umpeen (palautusarvo on silloin 0). Funktio palauttaa –1 virhetilanteessa (esimerkiksi kun signaali keskeyttää odotuksen, errno on silloin EINTR. HL

12 Makrot tyypille fd_set
Tyyppi fd_set esittää deskriptorijoukkoa (sisältää bitin kullekin deskriptorille). Tyypille on määritelty seuraavat makrot: FD_ZERO(fd_set *fdset); FD_SET(int fd, fd_set *fdset); FD_CLR(int fd, fd_set *fdset); FD_ISSET(int fd, fd_set *fdset); Makro FD_ZERO tekee joukon fdset tyhjäksi eli nollaa kaikkia deskriptoreita vastaavat bitit. Makro FD_SET lisää deskriptorin fd joukkoon fdset (asettaa vastaavan bitin). Makro FD_CLR poistaa deskriptorin fd joukosta fdset (nollaa vastaavan bitin). Makrolla FD_ISSET voidaan selvittää, onko parametrilla fd ilmoitettu deskriptori joukossa fdset ( eli onko deskriptoria vastaava bitti päällä deskriptori-joukossa fdset. Huomautus. Palauta mieleen ja vertaa funktioihin: int sigemptyset(sigset_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); int sigismember(const sigset_t *set, int signo); HL

13 Ongelman ratkaisu multipleksauksella
Nyt voimme ratkaista ongelman sivulla 8 seuraavasti. int main (void) { char chr_from_kb, chr_from_line; int fd_kb, fd_line, fd_display; fd_set fdset; int n; while (...) { FD_ZERO(&fdset); FD_SET(fd_kb, &fdset); FD_SET(fd_line, &fdset); n = select(max(fd_kb, fd_line) + 1, &fdset, NULL, NULL, NULL); if (n > 0) { if (FD_ISSET(fd_kb, &fdset)) { read(fd_kb, &chr_from_kb, 1); write(fd_line, &chr_from_kb, 1); // Or do whatever necessary } if (FD_ISSET(fd_line, &fdset)) { read(fd_line, &chr_from_line, 1); write(fd_display, &chr_from_line, 1); } // inner if } // outer if } // while } // main HL

14 Multipleksauksen perusidea
Multipleksauksen perusidea on usein seuraava: while (1) { Aseta välitöntä lukemista tarvitsevat descriptorit joukkoon readfds. Aseta välitöntä kirjoittamista tarvitsevat deskriptorit joukkoon writefds. Kutsu funktiota select. Kun funktio select palaa 1) Tutki, mitkä deskriptorit ovat joukossa readfds ja tee niille lukuoperaatio. 2) Tutki, mitkä deskriptorit ovat joukossa writefds ja tee niille kirjoitusoperaatio. } Seuraavaksi siirrymme toiseen sivulla 9 mainittuun menetelmään eli asynkroniseen i/o:hon. Asynkronisen i/o:n perusidea on siinä, että käyttöjärjestelmää pyydetään ilmoittamaan signaalilla siitä, että filedeskriptorissa on data valmiina tai se on valmis vastaanottamaan tietoa. Perinteisessä unix-tavassa pyyntö käyttöjärjestelmälle tehdään kerran ja sen jälkeen se ilmoittaa signaalilla aina kun on valmista. Uudessa tavassa pyyntö kohdistuu vain yhteen i/o- operaatioon ja pyyntö on tehtävä aina uudelleen. HL

15 Asynkroninen i/o (perinteinen tapa)
Tässä esitellään ensin UNIXin perinteinen asynkroninen i/o, sitten POSIXin uusi tapa. Asynkroninen i/o tarkoittaa sitä, että ohjelma ei jää odottamaan i/o-toiminnan päättymistä, vaan ohjelman suoritus jatkuu. Käyttöjärjestelmä ilmoittaa ohjelmalle siitä, kun tieto on luettavissa laitteelta tai kun tieto voidaan kirjoittaa laitteelle. Ilmoitus tehdään esimerkiksi signaalilla. Tähän käytetään oletusarvoisesti signaalia SIGIO. Jotta asynkroninen i/o saadaan toimimaan, tarvitaan käytännössä seuraavat asiat: 1. Asetetaan signaalille SIGIO oma signaalinkäsittelijä, jossa luetaan laitteelta (kirjoitetaan laitteelle). 2. Ilmoitetaan käyttöjärjestelmälle se prosessi, jolle signaali pitää lähettää. Tämä tehdään funktiolla fcntl (komento F_SETOWN). 3. Laitetaan kyseinen filedeskriptori asynkroniseen tilaan (eli asetetaan päälle tiedostoflagi O_ASYNC). Tämäkin tehdään funktiolla fcntl (komento F_SETFL). Kohta 3. tarkoittaa itse asiassa pyyntöä kernelille toimittaa signaali aina, kun deskriptori valmis. Esimerkki seuraavalla sivulla. HL

16 Esimerkki (perinteinen tapa)
Allaolevassa esimerkissä näytetään kuinka standard input (näppäimistö) laitetaan asynkroniseen tilaan. void input_ready(int signo); int main(void) { int flags; //Asetetaan signaalinkäsittelijä signaalille SIGIO signal(SIGIO, input_ready); //Ilmoitetaan prosessi, jolle signaali lähetetään fcntl(STDIN_FILENO, F_SETOWN, getpid()); //Asetetaan laite asynkroniseen tilaan flags = fcntl(STDIN_FILENO, F_GETFL, 0); flags = flags | O_ASYNC; fcntl(STDIN_FILENO, F_SETFL, flags); while(1) { do_something(); sleep(1); } void input_ready(int sig) { char input[80]; int n; n = read(STDIN_FILENO, input, 80); if (n > 0 ) { input[n] = '\0'; printf("Input was %s\n", input); // to solve problem on page 8 read(fd_line, &chr_from_line, 1); write(fd_display, &chr_from_line, 1) // to solve problem on page 8 read(STDIN_FILENO, &chr_from_kb, 1); write(fd_display, &chr_from_kb, 1) HL

17 Asynchronous i/o ja terminaali
Puhuttaessa asynkronisesta i/o:sta terminaalien kohdalla pitää erottaa kanoninen moodi ja ei-kanoninen moodi. Ei-kanoninen moodi: Signaali saadaan, kun yksikin merkki on luettavissa input jonosta (type ahead puskurista). Kanoninen moodi Signaali saadaan, kun kanoninen rivi on valmis, ts. kun painetaan enter (esimerkki edellisellä sivulla toimii näin). Huomautus 1. Tämän vanhan tavan puutteita ovat: - toimii vain inputissa - hankala hoitaa useita deskriptoreja (kaikille sama signaali, signaaleilla ei jonoa). - ei ole standardi (BSD). Uusi POSIXin AIO (Asynchronous Input/Output) esitellään seuraavaksi. HL

18 POSIXin uusi asynkroninen i/o
POSIX määrittelee uuden tavan asynkronisen i/o:n hoitoon standardin laajennuksessa POSIX:AIO. Asynkronisen i/o:n perusidea: 1. Prosessi käynnistää yhden asynkronisen lukemisen tai kirjoituksen funktiolla aio_read tai aio_write. Nämä funktiot eivät blokkaa. 2. Prosessi jatkaa käskyjen suorittamista ja i/o tapahtuu ohjelman suorituksen rinnalla. (Prosessi ei mene odotustilaan.) 3. Prosessi saa tietää i/o:n valmistumisesta joko signaalin avulla tai kysymällä sitä. POSIX määrittelee viisi uutta funktiota asynkronista i/o:ta varten: aio_read aloittaa asynkronisen luvun. aio_write aloittaa asynkronisen kirjoituksen. aio_error palauttaa i/o:n tilan (tai virheen). aio_return palauttaa luettujen tai kirjoitettujen tavujen määrän. io_cancel lopettaa käynnissä olevan i/o:n Funktioilla aio_read ja aio_write on yksi parametri, joka on osoite tietueeseen struct aiocb. <aio.h> int aio_read(struct aiocb *aiocbp); int aio_write(struct aiocb *aiocbp); Huom. Pitää incluudata <aio.h> ja linkata real-time library antamalla -lrt gcc:n komentoon. HL

19 struct aiocb Tietueen struct aiocb määrittely on muotoa:
int aio_fildes; // file descriptor volatile void *aio_buf; // buffer address size_t aio_nbytes; // number of bytes off_t aio_offset; // file offset int aio_reqprio; // request priority struct sigevent aio_sigevent; // signal no and value int aio_lio_opcode; // listio operation (multiple i/o // requests) }; Kolme ensimmäistä kenttää vastaavat funktioiden read ja write parametreja. Kenttä aio_sigevent (tietue) määrittelee kuinka prosessille ilmoitetaan i/o:n päättymisestä. Jos tietueen aio_sigevent jäsen sigev_notify on SIGEV_NONE signaalia ei lähetetä. Jos tämä jäsen on SIGEV_SIGNAL, kernel lähettää signaalin, jonka numero on määrätty tietueen aio_sigevent kentässä sigev_signo. Tietueen struct sigevent määrittely on muotoa: struct sigevent { int sigev_notify; //notification type int sigev_signo; //signal number union sigval sigev_value; //signal value HL

20 union sigval Unionin sigval määrittely on muotoa: union sigval {
int sival_int; void *sival_ptr; }; Tietueen struct sigevent kolmas kenttä sigev_value on lähetettävän signaalin mukana välitettävä parametriarvo. Tämä liittyy reaaliaikasignaaleihin, joiden signaalinkäsittelijäfunktiolla on signaalinumeroparametrin lisäksi toinen parametri, jolla käsittelijälle saadaan välitettyä lisätietoa. Koska tämä parametri on union, välitettävänä arvona voidaan käyttää int tyyppistä tietoa tai void* tyyppistä tietoa. Reaaliaikasignaaleihin palataan myöhemmin. HL

21 Funktiot aio_error ja aio_return
I/o:n etenemistä voidaan monitoroida funktiolla aio_error. Funktio aio_error palauttaa 0, jos i/o on päättynyt onnistuneesti ja EINPROGRESS, jos operaatio on vielä käynnissä. Jos operaatio on päättynyt virheeseen, funktio palauttaa virhekoodin, joka vastaa systeemikutsujen read ja write tuottamia virhekoodeja muuttujassa errno. int aio_error(const struct aiocb *aiocbp); Kun i/o on päättynyt onnistuneesti, funktiolla aio_return voidaan selvittää, montako merkkiä on saatu luettua tai kirjoitettua. <aio.h> ssize_t aio_return( struct aiocb *aiocbp); Katso seuraavalla sivulla oleva esimerkki, joka valaisee kuinka asynkroninen i/o käynnistetään, kuinka sen kanssa rinnan tehdään jotakin muuta ja kuinka odottaa i/o:n valmistumista sitten kun ohjelma ei enää voi jatkaa ilman i/o:n tulosta. Tämä ensimmäinen esimerkki käyttää pollausta i/o:n päättymisen odottamiseen. Se ei ole usein hyvä ratkaisu. Seuraavan sivun esimerkin päätarkoitus on valaista kuinka yllämainittuja aio-funktioita käytetään. Katso toinen esimerkki, jossa nähdään kuinka signaaleja käytetään i/o:n päättymisen ilmaisemiseen linkistä asynch_io_new_1.docx. HL

22 Esimerkki 1 int main(void) { char chr; struct aiocb aiocb; int r, n; //Fill i/o control block aiocb.aio_fildes = STDIN_FILENO; aiocb.aio_buf = &chr; aiocb.aio_nbytes = 1; aiocb.aio_offset = 0; aiocb.aio_reqprio = 0; aiocb.aio_sigevent.sigev_notify = SIGEV_NONE ; aiocb.aio_lio_opcode = 0; //Initiate i/o aio_read(&aiocb); // Do something parallel with the i/o sleep(2); // demonstrates doing something // Wait for the completion of the i/o while ( aio_error(&aiocb) == EINPROGRESS); // Test for error if ((r = aio_error(&aiocb)) != 0) { printf("%s\n", strerror(r)); exit(0); } else // i/o was successful n = aio_return(&aiocb); if (n > 0) // we really read something printf("I/o completed. The result was %c (code %d)\n", chr, chr); else // n== 0 (end of file ) printf("End of file received\n"); return 0; Tämän esimerkin tarkoitus on pääasiassa valaista funktioiden aio_error ja aio_return käyttöä. HL

23 Vähän lisää ei-blokkaavasta i/o:sta 1
Verrataan vielä asynkronista i/o:ta ja ei-blokkaavaa i/o:ta. Molemmille on yhteistä, että i/o-käsky ei blokkaa, vaan palaa heti. Erilaista on, että 1) Ei-blokkaavassa i/o:ssa palattaessa i/o joko saatiin tehtyä tai ei saatu tehtyä. Jos sitä ei saatu tehtyä pyyntö ei enää ole voimassa. 2) Asynkronisesta pyynnöstä palattaessa ei vielä tiedetä mitään kuinka tai koska i/o tulee päättymään. Ei-asynkronisessa i/o:ssa myös output voi blokata, jos deskriptoria ei erityisesti aseteta ei-blokkaavaan moodiin. Kertauksena, että ei-blokkaava moodi voidaan asettaa päälle flagillä O_NONBLOCK 1. Avattaessa tiedostoa open-funktiolla. 2. Myöhemmin funktiolla fcntl. Silloin i/o-funktiot (esim read ja write) palauttavat arvon –1 ja errno on EAGAIN (POSIX.1), jos ne muuten blokkaisivat. Blokkaaminen tapahtuisi mm, jos - Ei ole luettavaa. - Putkeen ei mahdu kirjoitettavaa. - Mikään deskriptori ei ole valmis select-kutsussa. HL

24 Vähän lisää ei-blokkaavasta i/o:sta 2
Esimerkki. Jos näyttöön lähetetään suuri tiedosto (vaikka tavua), niin blokkaavassa moodissa write odottaa, kunnes kaikki tavut on saatu kirjoitettua. Ei-blokkaavassa moodissa, write kirjoittaa niin monta tavua kuin sisäisiin puskureihin mahtuu ja palauttaa kirjoitettujen tavujen määrän (errno on 0). Jos heti yritetään kirjoittaa perään vielä kirjoittamattomia tavuja, write palauttaa –1 ja errno on EAGAIN. Tämän esimerkin toteuttava ohjelma seuraavalla sivulla. Ohjelmaa ajettaessa eräässä ympäristössä saatiin seuraavat tulokset: 1. Ohjelma kirjoitti ensimmäisellä write käskyllä byteä tiedostoon. 2. Sitten ohjelma yritti useita kertoja kirjoitusta palaten virheellä EAGAIN. 3. Joidenkin yritysten jälkeen write onnistui siten, että saatiin kirjoitettua 4095 tavua (vaikka yritettiin paljon enemmän eli koko loppuosaa). 4. Sitten tapahtui taas kuten kohdassa 2 ja sitten kohdassa 3. Tätä jatkui kunnes lopuksi kirjoitettiin 820, jolloin kaikki oli saatu kirjoitettua. HL

25 Esimerkkiohjelma edellisestä
#define SIZE //includet puuttuu ”tilanpuutteen” takia char buffer[SIZE]; int main(void){ char *p; int bytes_left, n, i, flags; FILE *logf; logf = fopen("loki.txt", "w"); //initialize array for (i = 0 ; i < SIZE ; i++ ) buffer[i] = 'A' + i % ('Z' - 'A' + 1); //set (terminal in non-block mode flags = fcntl(STDOUT_FILENO, F_GETFL, 0); flags |=O_NONBLOCK; flags = fcntl(STDOUT_FILENO, F_SETFL, flags); //send big buffer to the display p = buffer; bytes_left = SIZE; while (bytes_left > 0) { n = write(STDOUT_FILENO, p , bytes_left); if (n >= 0) { bytes_left-=n; p+=n; fprintf(logf, "Bytes sent %d %c%c", n, 13, 10); } if (n < 0) { fprintf(logf, "Error, errno is %d %c%c", errno, 13, 10 ); sleep(1); fclose(logf); HL


Lataa ppt "Terminaaliasetukset Sivut 1 – 7 käsittelevät terminaali i/o:ta. Niitä ei käsitellä tunnilla. Asioiden tunteminen voi olla hyödyllistä, sillä niitä voidaan."

Samankaltaiset esitykset


Iklan oleh Google