Suportul pentru oricare dintre aceste apeluri necesită suport din partea driverului dispozitivului. Această asistență (pentru toate cele trei apeluri) este furnizată utilizând metoda driver poll (polling). Această metodă are următorul prototip:
semnul nesignificat int (* poll) (fișier struct * filp, poll_table * așteptați);
Metoda șoferului se numește ori de câte ori programul de spațiu utilizator execută apelul de sistem de sondaj. selectați sau epoll cu descriptorul de fișier asociat cu driverul. Metoda dispozitivului este responsabilă pentru aceste două acțiuni:
1. Apel poll_wait pentru unul sau mai multe cozi care pot indica o modificare a statutului studiului. În cazul în care nu există descriptori de fișier disponibil în prezent pentru I / O, kernel-ul face ca procesul să aștepte în coada de așteptare pentru toți descriptorii de fișier trecut la apelul de sistem.
2. Returnați o mască bit care descrie operațiile (dacă există) care pot fi executate imediat fără blocare.
Ambele aceste operațiuni sunt de obicei simple și foarte asemănătoare de la un conducător auto la altul. Se presupune totuși că numai șoferul poate furniza aceste informații și, prin urmare, ele trebuie să fie implementate individual de către fiecare șofer.
void poll_wait (fișier struct * filp, wait_queue_head_t * wait_queue, poll_table * așteptați);
A doua sarcină se realizează prin metoda sondajului returnând o descriere a unei descrieri bitmase, care operații pot fi terminate imediat; este de asemenea ușor. De exemplu, dacă dispozitivul are date, citirea se va face fără somn; metoda de sondaj ar trebui să arate această stare de lucruri. Pentru a indica posibilele operații, se utilizează mai multe steaguri (definite prin
Acest bit trebuie să fie setat dacă datele "normale" pot fi citite. Dispozitivul care trebuie citit revine (POLLIN | POLLRDNORM).
Acest bit indică faptul că datele disponibile în afara legăturii logice (din canalul auxiliar auxiliar) sunt disponibile pentru citire de pe dispozitiv. Utilizată în prezent numai într-un singur loc în kernel-ul Linux (codul DECnet) și, de regulă, nu se aplică driverelor de dispozitive.
Când procesul de citire a acestui dispozitiv vede sfârșitul fișierului, driverul trebuie să seteze POLLHUP (se blochează). Procesul de apelare selectă spune că dispozitivul este lizibil, așa cum este dictat de funcționalitatea selectată.
A apărut o eroare la dispozitiv. Atunci când se apelează sondajul, dispozitivul este raportat ca citit și scriitor, iar citirea și scrierea returnează codul de eroare fără blocare.
Acest bit este setat în valoarea returnată dacă acest dispozitiv poate fi scris fără blocare.
Acest bit are același înțeles cu POLLOUT și, uneori, acesta este cu adevărat același număr. Dispozitivul care urmează să fie scris se întoarce (POLLOUT | POLLWRNORM).
Este de remarcat faptul că repetarea POLLRDBAND și POLLWRBAND face sens doar pentru descriptorii de fișiere asociate cu prize: drivere de dispozitiv nu se va folosi în mod normal, aceste steaguri.
Sondajul de opinie presupune mult spațiu pentru ceea ce este relativ ușor de utilizat în practică. Luați în considerare punerea în aplicare a metodei sondajului în scrutine:
static unsigned int scull_p_poll (fișier struct * filp, poll_table * așteptați)
struct scull_pipe * dev = fișier-> date private;
masca nesemnată int = 0;
* Acest tampon este circular; este considerat complet
* dacă "wp" este direct în spatele "rp" și gol dacă
poll_wait (filp, dev-> inq, așteptați);
poll_wait (filp, dev-> outq, așteptați);
dacă (dev-> rp! = dev-> wp)
mască | = POLLIN | POLLRDNORM; / * citit * /
mască | = POLLOUT | POLLWRNORM; / * este scris * /
Acest cod adaugă pur și simplu două cozi de așteptare la poll_table. apoi stabilește măștile de biți corespunzătoare, în funcție de faptul dacă datele pot fi citite sau scrise.
Codul de sondaj indicat nu acceptă suportul pentru "sfârșitul fișierului", deoarece scoolpipe nu acceptă starea "sfârșitul fișierului". Pentru majoritatea dispozitivelor reale, sondajul trebuie să returneze POLLHUP. dacă datele nu mai sunt (sau nu vor). Dacă apelantul a utilizat apelul de sistem selectat. acest fișier este raportat ca fiind ușor de citit. Indiferent dacă au fost utilizate sondajul sau selecția. Aplicația știe că poate duce la citire fără a aștepta pentru totdeauna și metoda de citire va reveni semnând 0 despre sfârșitul fișierului.
Cu FIFO-urile reale, de exemplu, cititorul vede sfârșitul fișierului atunci când toți scriitorii închid acest fișier, în timp ce cititorul scullpipe nu vede niciodată sfârșitul fișierului. Comportamentul este diferit, deoarece FIFO este proiectat să fie un canal de comunicare între două procese, iar scoolpipe este un coș de gunoi în cazul în care toată lumea poate pune date în timp ce există cel puțin un cititor. În plus, nu are sens să reimplementăm ceea ce este deja disponibil în kernel, deci am ales un alt comportament pentru implementare în exemplul nostru.
Punerea în aplicare a „sfârșitul fișierului“, precum și în FIFO, ar fi de a face o verificare de fond dev-> nwriters în citit și în sondaj și să raporteze „sfârșitul fișierului“ (așa cum este descris mai sus), în cazul în care nu există nici un proces care are dispozitivul deschis pentru scriere. Din păcate, atunci când o astfel de punere în aplicare, în cazul în care un cititor deschis dispozitiv scullpipe înainte de a scriitorului, el va vedea „sfârșitul fișierului“, fără a avea o șansă să aștepte pentru date. Cea mai bună modalitate de a rezolva această problemă va fi blocarea deschisă. așa cum fac FIFO-urile reale; această sarcină rămâne un exercițiu pentru cititor.
Interacțiunea cu citirea și scrierea
Scopul sondajului și selectarea apelurilor este de a determina în prealabil dacă operația I / O este blocată. În acest sens, ele completează citirea și scrierea. Mai important, sondajul și selecția sunt utile deoarece permit aplicației să aștepte simultan mai multe fluxuri de date, deși nu folosim această funcție în exemplele de scule. Pentru a face munca corectă, avem nevoie de punerea corectă în aplicare a trei apeluri: deși următoarele reguli au fost deja mai mult sau mai puțin spuse, le-am însumat aici.
Citirea datelor de pe dispozitiv
• Dacă există date în memoria tampon de intrare, apelul pentru citire trebuie să revină imediat, fără o întârziere vizibilă, chiar dacă sunt disponibile mai puține date decât cererea aplicației, iar conducătorul auto este convins că datele rămase vor ajunge în viitorul apropiat. Puteți întoarce întotdeauna mai puține date decât cele solicitate, cel puțin un octet, dacă este convenabil pentru orice motiv (am făcut-o în căruță). În acest caz, sondajul ar trebui să returneze POLLIN | POLLRDNORM.
• În absența datelor din memoria tampon de intrare, citirea implicită trebuie blocată până când nu există cel puțin un octet. Pe de altă parte, dacă este instalat O_NONBLOCK. citirea este imediat returnată cu valoarea -EAGAIN (deși în unele versiuni mai vechi ale sistemului V în acest caz, returnează 0). În aceste cazuri, sondajul ar trebui să raporteze că dispozitivul nu poate fi citit până la primirea a cel puțin un octet. Imediat ce unele date apar în buffer, revenim la cazul anterior.
• Dacă suntem la sfârșitul fișierului, citiți trebuie să reveniți imediat cu valoarea returnată de 0, indiferent de O_NONBLOCK. sondaj în acest caz ar trebui să informeze POLLHUP.
Scrierea pe dispozitiv
• Dacă există un spațiu în bufferul de ieșire, scrierea trebuie să se întoarcă fără întârziere. Poate accepta mai puține date decât apelul solicitat, dar trebuie să accepte cel puțin un octet. În acest caz, sondajul raportează că dispozitivul poate fi scris, returnând POLLOUT | POLLWRNORM.
• Dacă bufferul de ieșire este plin, scrierea este blocată implicit până când se eliberează un spațiu. Dacă este instalat O_NONBLOCK. scrierea este returnată imediat cu valoarea -EAGAIN (vechiul sistem V returnat 0). În aceste cazuri, sondajul ar trebui să raporteze că fișierul nu poate fi scris. Dacă, pe de altă parte, dispozitivul nu poate accepta date suplimentare, scrieți returnări -ENOSPC ("Nu mai există spațiu pe dispozitiv") indiferent de setarea parametrului O_NONBLOCK.
• Nu așteptați niciodată în apelul de scriere să așteptați transferul de date înainte de a reveni, chiar dacă O_NONBLOCK este șters. Multe aplicații utilizează selectați. Pentru a determina dacă scrierea este blocată. Dacă dispozitivul este raportat ca disponibil pentru înregistrare, apelul nu trebuie blocat. Dacă programul care utilizează dispozitivul dorește să se asigure că datele din coadă din bufferul de ieșire sunt de fapt transmise, acest driver trebuie să furnizeze metoda fsync. De exemplu, un dispozitiv detașabil trebuie să aibă un punct de intrare al fsync.
Deși acesta este un set bun de reguli comune, trebuie să recunoașteți că fiecare dispozitiv este unic și că uneori regulile ar trebui modificate ușor. De exemplu, dispozitivele orientate spre scriere (cum ar fi unitățile de bandă) nu pot efectua scrierea parțială.
Resetați la disc în timpul ieșirii
Am văzut cum metoda de scriere însăși nu ia în considerare toate nevoile de transfer de date. Funcția fsync. Chemat printr-un apel de sistem cu același nume, umple acest decalaj. Metoda prototipului este
int (* fsync) (fișier struct * fișier, struct dentry * dentry, int datasync);
Dacă o aplicație trebuie să fie întotdeauna sigur că datele au fost trimise la aparat, metoda Fsync trebuie efectuată indiferent dacă O_NONBLOCK este setat. Apelarea Fsync ar trebui să se întoarcă numai atunci când datele din aparat au fost complet transferat (de exemplu, tamponul de ieșire este gol), chiar dacă este nevoie de ceva timp. datasync argument este utilizat pentru a distinge între apelurile sistem Fsync și fdatasync; ca atare, este de interes doar pentru codul de sistem de fișiere și poate fi ignorat de către conducătorii auto.
Structura de date de bază
Implementarea reală a sondajului și a apelurilor selectate de sistem este suficient de simplă pentru cei care sunt interesați de modul în care funcționează; Epoolul este un pic mai complicat, dar construit pe același mecanism. Ori de câte ori o aplicație de utilizator cheamă sondaj. selectați sau epoll_ctl (* Aceasta este o funcție care creează o structură internă de date pentru viitoarele apeluri epoll_wait). kernel-ul solicită metoda de sondaj a tuturor fișierelor la care se face referire prin apelul sistem, trecând la fiecare dintre ele același poll_table. Structura poll_table este doar o împachetare în jurul unei funcții care creează o structură reală de date. Pentru sondaj și selectare, această structură este o listă legată de pagini de memorie care conțin structurile poll_table_entry. Fiecare poll_table_entry conține o structură de fișier, iar indicatorii wait_queue_head_t sunt transmiși la poll_wait împreună cu obiectul de coadă asociat. Apelul poll_wait uneori adaugă și acest proces la această coadă de așteptare. În general, întreaga structură trebuie să fie întreținută de kernel, astfel încât acest proces să poată fi eliminat din toate aceste cozi înainte de sondaj sau să selecteze returnările.
Dacă niciunul dintre șoferii chestionați nu indică faptul că I / O poate apărea fără blocare, apelul de sondare pur și simplu doarme, până când una din (probabil multe) aștepte cozi se trezește.
Interesant în implementarea sondajului este că metoda conducătorului auto poate fi apelată cu pointerul NULL ca argument argument poll_table. Această situație poate apărea din mai multe motive. Dacă aplicația a sunat la sondaj. a trecut valoarea 0 (ceea ce indică faptul că nu trebuie așteptat), nu există niciun motiv pentru a colecta coada de așteptare, iar sistemul nu face nimic. Pointerul poll_table este, de asemenea, imediat setat la NULL după orice sondare a șoferului, indicând că I / O este posibil. Din moment ce nucleul știe că nu vor exista așteptări, nu creează o listă de cozi de așteptare.
După terminarea apelului de sondaj, structura poll_table este eliberată și toate obiectele de așteptare așteptate anterior adăugate la tabela de sondare (dacă există) vor fi eliminate din tabel și așteptările lor de așteptare.
Am încercat să prezentăm în Figura 6-1 structurile de date implicate în anchetă; această cifră reprezintă o reprezentare simplificată a unei structuri de date reale deoarece ignoră natura mai multor pagini a tabelului de votare și ignoră indicatorul de fișier care face parte din fiecare poll_table_entry. Cititorul interesat de implementarea reală este încurajat să se uite
Figura 6-1. Structura datelor se ascunde în spatele sondajului
În acest moment puteți înțelege motivul pentru noul sistem de apel epoll. În mod obișnuit, un sondaj sau un apel select include doar un număr mic de descriptori de fișiere, astfel încât costul creării unei structuri de date este mic. Cu toate acestea, există aplicații care funcționează cu mii de descriptori de fișiere. Totuși, crearea și ștergerea acestei structuri de date între fiecare operațiune I / O devine prohibitiv de costisitoare. Familia apelului de sistem epoll permite aplicațiilor de acest tip să creeze o singură dată o structură de date interne a kernel-ului și să o folosească de mai multe ori.