Terminal Input/Output (I/O)
Postoje dva različita načina rada za terminalni ulaz/izlat (terminal I/O)
Ako ne navedemo drugačije, inicijalne postavke su podešene na standardni način. Npr. vi-editor koristi nekanonski način rada (komande mu mogu biti pojedini znakovi i ne trebaju završavati sa novim retkom). Također vi ima promijenjena značenja nekih kontrolnih znakova, pa tako CTRL+Z nema svoje standarno značenje, a CTRL+D umjesto kraja unosa ima za rezultat pomak za polovicu ekrana.
Potonje postavke, odnosno onemogućavanje nekih standarnih kontrolnih znakova može se izvesti neovisno u kojem se načinu procesiranja ulaza nalazimo. Također postoje i neke druge mogućnosti. (npr. tipkanje lozinke se ne bi smjelo vidjeti na ekranu)
Za sve ovo gore navedeno, tj. za podešavanje karakteristika terminalnih uređaja unutar programa vršit će se kroz sljedećeu strukturu definiranu u zaglavlju <termios.h>.
Popis svih terminal flagova možete naći npr. ovdje:
http://docsrv.caldera.com/cgi-bin/man/man?termio+7
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] /*kontrolni znakovi*/
}
Za primjere koji će sljediti od interesa će biti local flags jer će se pomoću njih moći npr. isključiti prikaz otipkanih znakova na ekranu (ECHO), promjena načina procesiranja inputa,...)
Tip tcflag_t je dovoljno veliki da drži vrijednosti svih flagova i obično je tipa long.
Polje cc_c (tipa cc_t koji je obično unsigned char) sadrži specijalne znakove koje možemo promijeniti .
Promjene i uvid u stanja unutar programa možemo vršiti koristeći funkcije tcgetattr i tcsetattr. U komandoj liniji možemo se služiti naredbom stty (otipkamo li stty –a dobit ćemo popis trenutnih opcija)
int tcgetattr(int filedes, struct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios *termptr);
vraćaju 0 ako je OK, -1 u slučaju greške
Napomena: funkcija tcsetattr će vratiti OK čak i ako ne uspije postavljanje svih opcija.
I jedan i druga uzimaju pokazivač na termios strukturu i preko njega vraćaju trenutne vrijednosti odnosno postavljau nove. Budući da i jedan i druga funkcija rade samo na terminalnih uređajima ako se filedes ne odnosi na terminal, vraća se -1 i varijabla errno se postavlja na ENOTTY. Da li je neki file descriptor terminal možemo provjeriti koristeći funkciju isatty(filedes).
Argument opt određuje kada želimo primijeniti nove postavke na terminal: TCSANOW (odmah), TCSADRAIN(nakon što cijeli izlaz bude odaslan), TCSAFLUSH(nakon što cijeli output bude poslan, ali svi neprocesirani ulazni podaci su odbačeni).
Kontrolni terminal (obično /dev/tty) možemo dohvatiti pomoću funkcije
char *ctermid(char *ptr)
Također u daljnim primjerima korisne će nam biti i sljedeće funkcije:
char *ttyname(int filedes)
int fileno(FILE *fp)
//jednostavni primjer: promjenimo kontrolni znak za EOF u CTRL+B i onemogućimo INTR //karakter.
//Primjer : noecho.c -> u primjeru se isključi lokalni echo tako da se na ekranu ne vide otipkani znakovi (korisno i poželjno za unos lozinki i sl).
Nekanonski način možemo postaviti tako da isključimo zastavicu ICANON u c_lflag polju termios strukture. U nekakonskom načinu rada sljedeći specijalni znakoi se ne procesiraju: ERASE, KILL, EOF, NL, EOL, EOL2, CR, REPRINT, STATUS i WERASE.
Dok je kanonskom načinu rada očito pristizanje podataka jasno (pritiskom na enter, redak po redak), nameće se pitanje kao u nekanonskom načinu rada znati kada je podatak pristigao.
Rješenje koristi varijable u polju c_cc strukture termios: MIN i TIME. Ova dva elementa se nalaze u polju na indeksima VMIN i VTIME.
MIN određuje minimalan broj byetova prije nego read završi. TIME određuje broj desetinski sekundi vremena u kojem će se čekati na podatke. Moguće su sljedeće četiri kombinacije:
//Primjer : nocanon.c
I/O multiplexing
Promotrimo sljedeći problem. Što ako ne znamo da li na nekom ulaznu ima podataka, a ne želimo da read ostane blokiran. Što npr. ako čekamo na podatek da 2 različita ulaza? Postoji više načina:: moguće je npr. postaviti da je određeni file deskriptor non-blocking ( npr. fcntl(fd, F_SETFL, O_NONBLOCK)), međutim onda imamo beskorisno utrošeno procesorsko vrijeme. Drugi način je pomoću signala (npr. SIGUSR1), ali i to ima svojih mana (svi dobiju signal, i dalje ne znamo koji je ulaz spreman, ako ih ima više i sl.)
U primjeru iomult.c glavni program (roditelj čeka na podatke iz procesa djeteta, ali istovremeno i sa tipkovnice.Mogući slučaj je i timeout.
Da bi ovo realizirali služi nam fukcija select. Da bi je koristili treba uključiti sljedeća zaglavlja: <sys/types.h>, <sys/time.h>, <unistd.h>.
int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr);
funkcija vraća broj spremnih file deskriptora, 0 u slučaju timeouta, -1 u slučaju greške
Promotrimo argumente funkcije select. Zadnji parametar određuje koliko želimo čekati:
struct timeval{
long tv_sec; /*broj sekundi*/
long tv_usec; /*broj mikrosekundi*/
}
Moguća su 3 uvjeta:
Tri srednja argumenta, readfds, writefds, exceptfds su pokazivači na skup deskriptora. Ova 3 skupa određuju za koje deskriptore smo zainteresirani i u kojim uvjetima. Deskriptore pospremimo u sljedeći tip podataka: fd_set. Jedino što možemo sa tim tipom podataka raditi je:
a) alocirati varijablu tog tipa
b) pridružiti varijablu ovog tipa nekoj drugoj varijabli tog tipa
c) koristiti jedan od sljedeća 4 makroa:
FD_ZERO(fd_set *fdset)
/*čisti sve bitove u fdset*/
FD_SET(int fd, fd_set *fdset)
/*uključuje bit za fd u skupu fdset*/
FD_CLR(int fd, fd_set *fdset)
/*isključuje bit za fd u skupu fdset*/
FD_ISSET(int fd, fd_set *fdset)
/*testira bit od fd u skupu fdset*/
//Primjer: iomult.c