CATEGORII DOCUMENTE |
Stream-uri - UNIX
Schema de implementare a driverelor de dispozitiv, sufera din pricina unor schimbari ce apar de-a lungul anilor. Diferite drivere tind sa dubleze functionalitatea, mai ales driverele care implementeaza protocoale de retea, ceea ce implica o parte de control de dispozitiv si o parte de protocol. Desi partea de protocol ar trebui sa fie comuna tuturor dispozitivelor din retea, acesta nu este cazul real din practica, deoarece nucleul nu furnizeaza mecanisme adecvate pentru folosire in comun. De exemplu :listele de caractere sunt folositoare in cazul buffer-arii, dar ele sunt costisitoare datorita manipularii caracter cu caracter. Incercari de a ocoli acest mecanism pentru performante mai mari cauzeaza scaderea modularitatii subsistemului I/O.
Lipsa generalitatii la nivel de driver filtreaza nivelul comenzilor utilizator unde mai multe comenzi pot indeplini functii logice comune dar peste medii diferite.
Stream-urile au fost implementate recent de Ritchie pentru a furniza o mai mare modularitate si flexibilitate subsistemului de I/O. Descrierea de aici se bazeaza pe lucrarea lui Ritchie, desi implementarea in System V difera putin. Un stream e o conexiune full-duplex intre un proces si driverul unui dispozitiv. El consta dintr-un set de perechi de cozi inlantuite liniar, un membru al perechii pentru intrare si celalalt pentru iesire. Cand un proces scrie date intr-un stream , nucleul trimite datele spre cozile de iesire. Cand un driver de dispozitiv receptioneaza datele introduse, el trimite datele spre cozile de intrare pentru un proces cititor. Cozile transmit mesajele cozilor adiacente in concordanta cu interfata definita. Fiecare pereche este asociata cu o instanta a unui modul nucleu, cum ar fi driver, disciplina de linie, sau protocol si modulele manipuleaza datele transmise prin cozile lor.
Fiecare coada este o structura de date ce contine urmatoarele elemente:
- o procedura de deschidere apelata in timpul apelului sistem open,
- o procedura de inchidere apelata in timpul apelului sistem close,
- o procedura put apelata pentru introducerea unui mesaj in coada,
- o procedura "service " apelata cand o coada este planificata sa se
execute,
- un pointer la urmatoarea coada din stream,
- un pointer la o lista de mesaje ce asteapta servirea,
- un pointer la o structura de date privata care pastreaza starea cozii,
- flag-uri si indicatori de tip limita superioara(inferioara) folositi pentru
controlul fluxului, planificare si pastrarea starii cozii.
Nucleul aloca perechi care sunt adiacente in memorie. Din acest motiv, o coada poate gasi usor celalalt membru al perechii ( figura 6.20).
Figura 6.20. Un stream dupa deschidere
Un dispozitiv cu driver de tip stream este dispozitiv de tip caracter. El are un camp special in tabela de comutare a dispozitivelor de tip caracter care pointeaza spre structura de initializare a stream-ului care contine adresele rutinelor si indicatorii limita superioara si limita inferioara. Cand nucleul executa apelul sistem open si descopera ca fisierul dispozitiv este de tip caracter, el examineaza noul camp in tabela de comutare a dispozitivelor de tip caracter . Daca acolo nu exista nici o intrare driverul nu este de tip stream, iar nucleul urmareste procedura uzuala pentru dispozitivele de tip caracter. Totusi pentru prima deschidere a unui driver de tip stream, nucleul aloca doua perechi de cozi, una pentru capul de stream, si cealalta pentru driver.
Modulul cap de stream este identic pentru toate instatierile stream-ului deschis: el are proceduri generice put si service si interfata cu modulele nucleu de nivel inalt care implementeaza apelurile sistem read,write si ioctl. Nucleul initializeaza structura de coada a driverului, asignand pointerii si copiind adresele rutinelor de driver dintr-o structura de initializare si invoca procedura de deschidere a driverului. Procedura de deschidere a driverului face initializarile uzuale dar salveaza si informatii pentru identificarea cozii cu care este asociat. In final, nucleul asigneaza un pointer special in inod din memorie pentru a indica capul de stream (figura 6.20 ). Cand un alt proces deschide dispozitivul, nucleul gaseste stream-ul alocat anterior prin pointerul din inod si invoca procedura de deschidere a tuturor modulelor din stream. Modulele comunica prin transmiterea de mesaje la modulele vecine. Un mesaj consta dintr-o lista inlantuita de antete de blocuri de mesaje; fiecare antet de bloc pointeaza spre locatia de inceput si sfarsit a datelor din bloc. Exista doua tipuri de mesaje (de control si de date ) identificate printr-un identificator de tip din antetul de mesaj. Mesajele de control pot rezulta din apelul sistem ioctl sau din conditii speciale, cum ar fi intreruperea unui terminal, si mesajele de date pot rezulta din apeluri sistem write sau sosiri din datelor de la un dispozitiv (figura 6.21).
Figura 6.21. Mesaje pentru stream-uri
Cand un proces scrie intr-un stream, nucleul copiaza datele din spatiul utilizator in blocurile de mesaje alocate de capul de stream. Modulul capul de stream invoca procedura put a urmatorului modul care poate procesa mesajul, trimite imediat mesajul la urmatoarea coada sau il retine pentru o procesare ulterioara. In ultimul caz modulul leaga antetele blocurilor de mesaje intr-o lista inlantuita, formand o lista dublu inlantuita ( Figura 6.21). Apoi seteaza un indicator in structura de date a cozii proprii pentru a indica daca are date de procesat si se autoplanifica pentru deservire. Modulul plaseaza coada intr-o lista inlantuita de cozi care solicita sa fie servite si invoca un mecanism de planificare; planificatorul apeleaza procedurile de deservire ale fiecarei cozi din lista. Nucleul ar putea planifica modulele prin intreruperi software ca in cazul invocarii functiilor in tabela "callout" ; rutina de tratare a intreruperii software apeleaza procedurile de deservire individual.
Figura 6.22. Punerea unui modul intr-un stream
Procesele pot pune modulele intr-un stream deschis prin apeluri ioctl. Nucleul insereaza modulul imediat sub capul de stream si seteaza pointerii cozii pentru a mentine structura de lista dublu inlantuita. Modulele finale ale stream-ului nu fac diferenta intre comunicarea cu capul de stream sau cu modul pus in lista. Interfata este procedura put a cozii urmatoare din stream; urmatoarea coada apartine modulului tocmai introdus. De exemplu, un proces poate sa introduca un modul de disciplina de linie intr-un stream de driver de terminal pentru stergerea si suprimarea procesarii de caractere (Figura 6.22); modulul de disciplina de linie nu are aceleasi interfete ca disciplinele descrise in sectiunea 6.3, dar functioneaza la fel. Fara modulul de disciplina de linie , driverul de terminal nu proceseaza caracterele introduse si astfel de caractere ajung nealterate la capul de stream. Un segment de cod care deschide un terminal si introduce o disciplina de linie poate arata astfel:
fd = open( "/dev/ttyxy",O_RDWR);
ioctl(fd,PUSH,TTYLD)
unde PUSH este numele comenzii si TTYLD este numarul care identifica modulul disciplinei de linii. Nu exista nici o restrictie asupra numarului de module ce pot fi introduse in stream. Un proces poate scoate (pop) module dintr-un stream intr-o ordine "lifo", folosind un alt apel ioctl:
ioctl (fd,POP,0)
Deoarece un modul de disciplina de linie implementeaza functii de procesare obisnuite ale terminalului, dispozitivul in discutie poate fi o conexiune de retea in locul unei conexiuni spre un dispozitiv terminal . Modulul disciplinei de linie lucreaza in acelasi mod, fara sa tina cont de modulele anterioare. Acest exemplu arata marea flexibilitate derivata din combinarea modulelor nucleului.
Un exemplu detaliat de stream
Pike descrie o implementare a terminalelor virtuale multiplexate utilizand stream-uri. Utilizatorul vede terminalele virtuale, fiecare ocupand o fereastra separata pe terminalul fizic. Desi lucrarea lui Pike descrie o schema pentru un terminal grafic inteligent, aceasta este valabila si pentru terminale obisnuite; fiecare fereastra ocupa intregul ecran si utilizatorul apasa o secventa de taste
pentru a comuta intre ferestrele virtuale.
Figura 6.23. Afisarea mai multor terminale virtuale pe un
terminal fizic
Figura 6.23 prezinta aranjarea proceselor si modulelor nucleului. Utilizatorul invoca un proces, mpx, pentru a controla terminalul fizic. Mpx citeste linia terminalului fizic si asteapta pentru aparitia evenimentelor de control, cum ar fi crearea de noi ferestre, comutarea controlului spre alta fereastra, stergerea unei ferestre si altele. Cand receptioneaza un eveniment de creare a unei ferestre noi, mpx creeaza un proces pentru a controla noua fereastra si comunica cu ea printr-un pseudo-terminal(pty). Un pty este un dispozitiv software ce opereaza in pereche. Iesirea alocata unui membru al perechii este trimisa intrarii celuilalt membru; intrarea este trimisa modulului anterior din stream.
/* presupunem ca descriptorii de fisiere 0 si 1 se refera deja la terminalul fizic tty */ for (;;) |
inchide descriptorii de fisier care nu sunt necesari; /* fiu */ |
deschide celalalt membru al perechii pseudo - terminalului, obtine stdin,stdout,stderr; pune disciplina de linie a terminalului; executa programul shell; } demultiplexeaza datele citite de la terminalul fizic, inlatura antetele si scrie la terminalul pty corespunzator ; continue; case terminal logic: decodifica antetul care indica pentru ce fereastra sunt datele ; scrie datele si antetul la terminalul fizic; continue; } |
} |
Figura 6.24. Pseudocod pentru multiplexarea ferestrelor
Pentru a seta o fereastra ( Figura 6.24), mpx aloca o pereche pty si deschide un membru stabilind un stream spre el (deschiderea driverului garanteaza ca pty nu a fost alocat anterior). Mpx creeaza un proces si acesta deschide celalalt membru al perechii pty. Mpx pune un modul de mesaj in stream-ul pty pentru a converti mesajele de control in mesaje de date ( explicat in paragraful urmator ) si procesul fiu pune un modul de disciplina de linie in stream-ul pty inainte sa apeleze exec pentru crearea shell-ului. Acest shell ruleaza acum pe un terminal virtual; pentru utilizator, nu este nici o diferenta intre terminalul virtual si un terminal fizic.
Procesul mpx este un multiplexor, care trimite iesirile de la terminalele virtuale la terminalul fizic si demultiplexeaza intrarea de la terminalul fizic la terminalul virtual corespunzator. Mpx asteapta sosirea datelor pe orice linie, folosind apelul sistem select. Cand datele sosesc de la terminalul fizic, mpx decide daca este un mesaj de control, care il informeaza ca trebuie sa creeze o noua fereastra sau sa stearga una veche, sau daca este un mesaj de date care trebuie sa fie trimis proceselor care citesc un terminal virtual. In ultimul caz, datele au un antet care identifica terminalul virtual tinta; mpx inlatura antetul din mesaj si scrie datele la stream-ul pty corespunzator. Driverul al lui pty dirijeaza datele prin disciplina de linie a terminalului la procesele cititor. Procedura inversa se executa cand un proces scrie terminalul virtual:mpx adauga un antet la date, informand terminalul fizic in care fereastra trebuie sa fie tiparite datele.
Daca un proces invoca un apel ioctl pe un terminal virtual, disciplina de linie a terminalului face setarile de terminal necesare pentru linia sa virtuala; setarile pot diferi pentru fiecare terminal virtual. Oricum, unele informatii trebuie sa fie trimise la terminalul fizic, depinzand de dispozitiv. Modulul de mesaj converteste mesajele de control care sunt generate de ioctl in mesaje de date potrivite pentru citirea si scrierea prin mpx si aceste mesaje sunt transmise la dispozitivul fizic.
Analiza stream-urilor
Ritchie mentioneaza ca el a incercat sa implementeze stream-uri numai cu proceduri put sau numai cu proceduri service. Totusi, procedura service este necesara pentru controlul fluxului, deoarece modulele trebuie uneori sa puna datele intr-o coada de asteptare daca modulele vecine nu pot primi, temporar, mai multe date. Procedura put este, de asemenea, necesara deoarece datele trebuie uneori sa fie livrate la un modul vecin. De exemplu, disciplina de linie a unui terminal trebuie sa reactioneze trimitand datele de intrare la terminal cat de repede este posibil. Ar putea fi posibil pentru un apel sistem write sa invoce procedura put a cozii urmatoare direct, fara sa fie nevoie de un mecanism de planificare.
Un proces se pune in asteptare daca cozile de iesire sunt supraaglomerate. Totusi, modulele nu pot sta in asteptare pe partea de intrare, deoarece ele sunt invocate printr-o rutina de tratare a intreruperii si un proces nevinovat ar putea fi pus in asteptare. Comunicarea intre module ar putea sa nu fie simetrica pe cele doua directii de intrare si de iesire.
Este preferabil, de asemenea, sa implementam fiecare modul ca un proces separat, dar folosirea unui numar mare de module ar putea cauza o supraincarcare a tabelei de procese. Ele sunt implementate cu un mecanism de planificare special - intrerupere software - independent de procesul planificator normal. Modulele nu se pot pune in asteptare, deoarece ele ar putea pune in asteptare un proces arbitrar ( unul care a fost intrerupt ). Modulele trebuie sa salveze informatiile lor de stare intern, facand sa se complice codul lor mai mult decat daca le-ar fi fost permis sa se puna in asteptare .
Exista unele anomalii in implementarea stream-urilor. Gestiunea proceselor este dificila, deoarece nu este necesar ca modulele sa ruleze in contextul procesului care foloseste stream-ul. Este fals sa presupunem ca toate procesele folosesc in comun in mod uniform executia modulelor din stream, deoarece unele procese pot cere folosirea unor protocoale complicate de retea, iar altele pot folosi discipline de linie de terminal simple.
Utilizatorii pot pune un driver de terminal in modul caracter, asa incat apelurile read sa se termine dupa un timp scurt daca datele nu sunt disponibile ( de exemplu, if newtty.c_ccºVMINþ = 0; in figura 6.17 ). Este dificil sa implementam aceasta caracteristica cu stream-uri, daca codul nu este introdus la nivelul capului de stream.
Stream-urile sunt conexiuni liniare si nu permit cu usurinta multiplexarea in nucleu. De exemplu, fereastra prezenta in sectiunea anterioara face multiplexarea intr-un proces de nivel utilizator.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1087
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved