Vstup/výstup dát

Väčšina predchádzajúcich článkov je venovaná optimalizácii výpočtového výkonu. Videli sme veľa príkladov ladenia zberu odpadu, cyklovania a rekurzívnych algoritmov a dokonca aj optimalizácie algoritmov na zníženie réžie behu.

Pre niektoré aplikácie poskytuje optimalizácia výpočtových aspektov len malé zvýšenie výkonu, pretože prekážkou v nich sú vstupno-výstupné operácie, ako napríklad prenos dát cez sieť alebo prístup na disk. Z našich skúseností môžeme povedať, že značná časť výkonnostných problémov nesúvisí s používaním neoptimálnych algoritmov alebo nadmernou záťažou procesora, ale s neefektívnym využívaním I/O zariadení. Pozrime sa na dve situácie, v ktorých môže optimalizácia I/O zlepšiť celkový výkon:

    Aplikácia môže zaznamenať veľké výpočtové preťaženie v dôsledku neefektívnych I/O operácií, ktoré zvyšujú réžiu. Horšie je, že preťaženie môže byť také veľké, že sa stane limitujúcim faktorom, ktorý bráni maximálnemu využitiu šírky pásma I/O zariadenia.

    I/O zariadenie nemusí byť plne využité alebo jeho schopnosti môžu byť premrhané v dôsledku neefektívnych programovacích vzorov, ako je odosielanie veľkého množstva údajov v malých častiach alebo nevyužitie celej šírky pásma.

Tento článok popisuje všeobecné koncepty I/O a poskytuje odporúčania na zlepšenie výkonu akéhokoľvek typu I/O. Tieto odporúčania platia rovnako pre sieťové aplikácie, procesy náročné na disk a dokonca aj programy, ktoré pristupujú k neštandardným, vysokovýkonným hardvérovým zariadeniam.

Synchrónne a asynchrónne I/O

Keď sú spustené v synchrónnom režime, I/O funkcie rozhrania Win32 API (napríklad ReadFile, WriteFile alebo DeviceloControl) blokujú vykonávanie programu, kým sa operácia nedokončí. Aj keď je tento model veľmi pohodlný na používanie, nie je príliš efektívny. V časových intervaloch medzi vykonaním po sebe nasledujúcich I/O požiadaviek môže byť zariadenie nečinné, to znamená, že nie je plne využité.

Ďalším problémom synchrónneho režimu je to, že vlákno vykonávania stráca čas pri akejkoľvek súbežnej I/O operácii. Napríklad v serverovej aplikácii, ktorá obsluhuje viacero klientov súčasne, môže byť možné vytvoriť samostatné vlákno vykonávania pre každú reláciu. Tieto vlákna, ktoré sú väčšinu času nečinné, plytvajú pamäťou a môžu vytvárať situácie mlátenie nití, keď mnohé vlákna vykonávania súčasne obnovia prácu po dokončení I / O a začnú bojovať o čas procesora, čo vedie k zvýšeniu prepínania kontextu za jednotku času a zníženiu škálovateľnosti.

I/O subsystém Windows (vrátane ovládačov zariadení) interne funguje v asynchrónnom režime – program môže pokračovať vo vykonávaní súčasne s I/O operáciou. Takmer všetky moderné hardvérové ​​zariadenia sú svojou povahou asynchrónne a na prenos údajov alebo určenie dokončenia I/O operácie nevyžadujú neustály dopyt.

Väčšina zariadení túto schopnosť podporuje priamy prístup do pamäte (Direct Memory Access, DMA) na prenos údajov medzi zariadením a pamäťou RAM počítača bez toho, aby sa vyžadovala účasť procesora na operácii, a generovať prerušenie po dokončení prenosu údajov. Synchrónny I/O, ktorý je interne asynchrónny, je podporovaný iba na aplikačnej vrstve Windows.

Vo Win32 sa volá asynchrónny I/O prekrývaný vstup/výstup (prekrývajúce sa I/O), porovnanie synchrónnych a prekrývajúcich sa I/O režimov je znázornené na obrázku nižšie:

Keď aplikácia odošle asynchrónnu požiadavku na vykonanie I/O operácie, Windows buď vykoná operáciu okamžite, alebo vráti stavový kód, ktorý indikuje, že operácia čaká na vybavovanie. Vlákno potom môže spustiť ďalšie I/O operácie alebo vykonať nejaké výpočty. Programátor má niekoľko spôsobov, ako organizovať prijímanie upozornení o dokončení I/O operácií:

    Udalosť Win32: Operácia čakajúca na túto udalosť sa vykoná po dokončení I/O.

    Volanie užívateľom definovanej funkcie s mechanizmom Volanie asynchrónnej procedúry (APC): Vlákno spustenia musí byť v stave čakania s upozornením.

    Prijímať upozornenia cez Porty na dokončenie I/O (IOCP): Toto je zvyčajne najúčinnejší mechanizmus. Budeme to podrobnejšie skúmať ďalej.

Niektoré I/O zariadenia (napríklad súbor otvorený v režime bez vyrovnávacej pamäte) môžu poskytnúť ďalšie výhody, ak aplikácia dokáže zabezpečiť, aby bol vždy prítomný malý počet čakajúcich I/O požiadaviek. Na tento účel sa odporúča najskôr zadať niekoľko požiadaviek na vykonanie I/O operácií a pre každú vykonanú požiadavku vytvoriť novú požiadavku. Tým sa zabezpečí, že ovládač zariadenia inicializuje ďalšiu operáciu čo najskôr bez čakania na ďalšiu požiadavku aplikáciou. Nepreháňajte to však s množstvom prenesených dát, pretože to spotrebuje obmedzené zdroje pamäť jadra.

Porty na dokončenie I/O

Windows podporuje efektívny mechanizmus na oznamovanie dokončenia asynchrónnych I/O operácií nazývaných I/O Completion Ports (IOCP). V aplikáciách .NET je dostupný prostredníctvom metódy ThreadPool.BindHandle(). Tento mechanizmus interne používa niekoľko typov v .NET, ktoré vykonávajú I/O operácie: FileStream, Socket, SerialPort, HttpListener, PipeStream a niektoré kanály .NET Remoting.

Mechanizmus IOCP zobrazený na obrázku vyššie komunikuje s viacerými I/O rukoväťami (sockety, súbory a špecializované objekty ovládačov zariadení), ktoré sú otvorené asynchrónne a so špecifickým vláknom vykonávania. Po dokončení vstupno-výstupnej operácie spojenej s takýmto popisovačom systém Windows pridá upozornenie na príslušný port IOCP a odošle ho na spracovanie pridruženému vláknu vykonávania.

Použitie oblasti vlákien na obsluhu upozornení a obnovenie vykonávania vlákien, ktoré spustili asynchrónne I/O operácie, znižuje počet prepnutí kontextu za jednotku času a zvyšuje využitie CPU. Nie je prekvapením, že vysokovýkonné servery, ako napríklad Microsoft SQL Server, používajú porty na dokončenie I/O.

Dokončovací port je vytvorený volaním funkcie Win32 API CreateIoCompletionPort, ktorému sa odovzdá maximálna hodnota súbežnosti (počet vlákien), kľúč dokončenia a voliteľný popisovač I/O objektu. Dokončovací kľúč je používateľom definovaná hodnota, ktorá sa používa na identifikáciu rôznych vstupno-výstupných rukovätí. Viaceré rukoväte môžete naviazať na rovnaký port IOCP opakovaným volaním funkcie CreateIoCompletionPort a odovzdaním rukoväte existujúcemu portu dokončenia.

Ak chcete vytvoriť spojenie so zadaným portom IOCP, užívateľské vlákna zavolajú funkciu GetCompletionStatus a čakať na jeho dokončenie. Vlákno vykonávania môže byť súčasne priradené iba k jednému portu IOCP.

Volanie funkcie GetQueuedCompletionStatus zablokuje spustenie vlákna, kým nedostane upozornenie (alebo uplynie časový limit), a potom vráti informácie o dokončenej vstupno-výstupnej operácii, ako je počet prenesených bajtov, kľúč dokončenia a štruktúra asynchrónnej vstupno-výstupnej operácie . Ak sú všetky vlákna priradené k I/O portu v čase upozornenia zaneprázdnené (to znamená, že na volanie GetQueuedCompletionStatus nečakajú žiadne vlákna), mechanizmus IOCP vytvorí nové vlákno spustenia až do maximálnej hodnoty súbežnosti. . Ak vlákno s názvom GetQueuedCompletionStatus a front upozornení nie je prázdny, funkcia sa okamžite vráti bez blokovania vlákna v jadre operačný systém.

Mechanizmus IOCP je schopný zistiť, že niektoré „zaneprázdnené“ vlákna skutočne vykonávajú synchrónny I/O a spustiť ďalšie vlákno, ktoré môže presiahnuť maximálnu hodnotu súbežnosti. Oznámenia je možné odosielať aj manuálne, bez vykonania I/O, volaním funkcie PostQueuedCompletionStatus.

Nasledujúci kód demonštruje príklad použitia ThreadPool.BindHandle() s deskriptorom súboru Win32:

Používanie systému; pomocou System.Threading; pomocou Microsoft.Win32.SafeHandles; pomocou System.Runtime.InteropServices; public class Extensions ( internal static extern SafeFileHandle CreateFile(string lpFileName, EFileAccess dwDesiredAccess, EFileShare dwShareMode, IntPtr lpSecurityAttributes, ECreationDisposition dwCreationDisposition, EFileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile); static unsafe extern bool WriteFile(SafeFileHandle hFile, byte lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten , System.Threading.NativeOverlapped* lpOverlapped); enum EFileShare: uint ( Žiadne = 0x00000000, Čítanie = 0x00000001, Zápis = 0x00000002, Otvoriť = 0x00000002, Otvoriť = 0x00000000 vytvoriť, Otvoriť (Vymazať) =D Vymazať =D Nový enum110004 , TruncateExisting = 5 ) enum EFileAttributes: uint ( // ... Niektoré príznaky sa nezobrazujú Normal = 0x00000080, Overlapped = 0x40000000, NoBuffering = 0x20000000, ) enum EFileAccess, 0x8 nezobrazujú sa príznaky EFileAccess:0 ...0x uint ( /0x uint) GenericWrite = 0x40000000, ) statické lo ng_numBytesWritten; // Brzda pre statický tok zápisu AutoResetEvent _waterMarkFullEvent; static int _pendingIosCount; const int MaxPendingIos = 10; // Rutina dokončenia, volaná I/O vláknami statické nebezpečné void WriteComplete(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) ( _numBytesWritten += numBytes; Overlapped ovl = Overlapped.Unpack(pOVERLAP) /pOVERLAP); / Informujte vlákno zapisovača, že počet čakajúcich I/O operácií // sa znížil na povolený limit, ak (Interlocked.Decrement(ref _pendingIosCount) = MaxPendingIos) ( _waterMarkFullEvent.WaitOne(); ) ) ) ) )

Najprv sa pozrime na metódu TestIOCP. Toto volá funkciu CreateFile(), čo je funkcia mechanizmu P/Invoke používaná na otvorenie alebo vytvorenie súboru alebo zariadenia. Ak chcete vykonávať I/O operácie v asynchrónnom režime, do funkcie sa musí odovzdať príznak EFileAttributes.Overlapped. Ak je úspešná, funkcia CreateFile() vráti deskriptor súboru Win32, ktorý naviažeme na port dokončenia I/O volaním ThreadPool.BindHandle(). Ďalej sa vytvorí objekt udalosti, ktorý sa používa na dočasné zablokovanie vlákna, ktoré spustilo I/O operáciu, ak existuje príliš veľa takýchto operácií (limit je nastavený konštantou MaxPendingIos).

Potom začne cyklus operácií asynchrónneho zápisu. V každej iterácii sa vytvorí vyrovnávacia pamäť s údajmi, ktoré sa majú zapísať a prekrytá štruktúra, ktorý obsahuje posun v rámci súboru (v tomto príklade sa zápis vždy vykonáva s posunom 0), popisovač udalosti, ktorý sa má odoslať po dokončení operácie (nepoužíva ho mechanizmus IOCP) a voliteľný používateľský objekt IAsyncResult Ten možno použiť na odovzdanie stavu do funkcie finalizátora.

Ďalej je zavolaná metóda Overlapped.Pack(), ktorá akceptuje baliacu funkciu a dátovú vyrovnávaciu pamäť. Vytvára ekvivalentnú nízkoúrovňovú I/O štruktúru v nespravovanej pamäti a pripája dátovú vyrovnávaciu pamäť. Uvoľnenie nespravovanej pamäte obsadenej nízkoúrovňovou štruktúrou a odpojenie vyrovnávacej pamäte sa musí vykonať manuálne.

Ak v tom istom čase neprebieha príliš veľa I/O, zavoláme WriteFile() a odovzdáme mu špecifikovanú nízkoúrovňovú štruktúru. V opačnom prípade počkáme, kým sa vyskytne udalosť, ktorá naznačuje, že počet čakajúcich operácií klesol pod hornú hranicu.

Funkciu dokončenia zápisu zavolá vlákno v oblasti vlákien dokončenia I/O hneď po dokončení operácie. Odovzdáva sa ukazovateľ na nízkoúrovňovú asynchrónnu I/O štruktúru, ktorú možno rozbaliť a skonvertovať na riadenú štruktúru Overlapped.

Stručne povedané, pri práci s vysokovýkonnými I/O zariadeniami používajte asynchrónne I/O s dokončovacími portami, buď priamo vytvorením a použitím vlastného dokončovacieho portu v nespravovanej knižnici, alebo priradením úchytov Win32 k dokončovaciemu portu v .NET. pomocou metódy ThreadPool.BindHandle().

Fond vlákien v .NET

Fond vlákien v .NET môže byť úspešne použitý na rôzne účely, z ktorých každý vytvára rôzne typy vlákien. V predchádzajúcej diskusii o paralelnom výpočte sme sa zoznámili s API fondu vlákien, kde sme ho používali na paralelizáciu výpočtových úloh. Fondy vlákien však možno použiť aj na riešenie iných druhov problémov:

    Pracovné vlákna dokážu spracovať asynchrónne volania delegátov používateľov (napríklad BeginInvoke alebo ThreadPool.QueueUserWorkItem).

    Vlákna dokončenia I/O môžu obsluhovať upozornenia z globálneho portu IOCP.

    Vlákna čakateľov môžu čakať na zaregistrované udalosti, čo vám umožňuje čakať na viacero udalostí v rovnakom vlákne naraz (pomocou WaitForMultipleObjects) až do hornej hranice Windowsu (maximálny počet čakacích objektov = 64). Príjem čakania na udalosť sa používa na organizáciu asynchrónnych I/O bez použitia dokončovacích portov.

    Vlákna časovačov čakajúcich na vypršanie platnosti viacerých časovačov naraz.

    Závity brány kontrolovať využitie procesora vláknami z fondu a tiež meniť počet vlákien (v rámci nastavených limitov), ​​aby sa dosiahol najvyšší výkon.

Je možné spustiť I/O operácie, ktoré sa zdajú byť asynchrónne, ale nie sú. Napríklad volanie delegáta ThreadPool.QueueUserWorkItem a následné vykonanie synchrónnej I/O operácie nie je skutočne asynchrónna operácia a nie je o nič lepšie ako vykonanie rovnakej operácie na bežnom vlákne vykonávania.

Pamäťová kópia

Nie je nezvyčajné, že fyzické I/O zariadenie vracia vyrovnávaciu pamäť údajov, ktoré sa kopírujú znova a znova, kým ich aplikácia nedokončí. Takéto kopírovanie môže spotrebovať značnú časť výpočtového výkonu procesora, preto sa mu treba vyhnúť, aby sa zabezpečila maximálna priepustnosť. Ďalej sa pozrieme na niekoľko situácií, kedy je zvykom kopírovať dáta, a zoznámime sa s technikami, ako sa tomu vyhnúť.

Nespravovaná pamäť

Práca s vyrovnávacou pamäťou v nespravovanej pamäti je v .NET oveľa náročnejšia ako so spravovaným bajtovým poľom, takže programátori často skopírujú vyrovnávaciu pamäť do riadenej pamäte pri hľadaní najjednoduchšieho spôsobu.

Ak vám funkcie alebo knižnice, ktoré používate, umožňujú explicitne špecifikovať vyrovnávaciu pamäť v pamäti alebo jej odovzdať funkciu spätného volania na pridelenie vyrovnávacej pamäte, prideľte riadenú vyrovnávaciu pamäť a pripnite ju do pamäte, aby k nej bolo možné pristupovať pomocou ukazovateľa aj riadenej referencie. Ak je vyrovnávacia pamäť dostatočne veľká (> 85 000 bajtov), ​​vytvorí sa v hromada veľkých predmetov (veľká hromada objektov), takže skúste znova použiť existujúce vyrovnávacie pamäte. Ak je opätovné použitie vyrovnávacej pamäte komplikované neistotou životnosti objektu, použite pamäťové oblasti.

V iných prípadoch, keď funkcie alebo knižnice samotné alokujú (nespravovanú) pamäť pre vyrovnávacie pamäte, môžete k tejto pamäti pristupovať priamo pomocou ukazovateľa (z nebezpečného kódu) alebo pomocou tried obalov, ako napr. UnmanagedMemoryStream a UnmanagedMemoryAccessor. Ak však potrebujete odovzdať vyrovnávaciu pamäť nejakému kódu, ktorý funguje iba na bajtových poliach alebo objektoch reťazcov, kopírovaniu sa možno vyhnúť.

Aj keď sa nemôžete vyhnúť kopírovaniu pamäte a niektoré alebo väčšina vašich údajov je odfiltrovaná na začiatku, môžete sa vyhnúť zbytočnému kopírovaniu tak, že pred kopírovaním skontrolujete, či sú údaje potrebné.

Export časti vyrovnávacej pamäte

Programátori niekedy predpokladajú, že bajtové polia obsahujú iba údaje, ktoré potrebujú, od začiatku do konca, čím nútia volajúci kód prerušiť vyrovnávaciu pamäť (prideliť pamäť pre nové bajtové pole a skopírovať len potrebné údaje). Túto situáciu možno často vidieť v implementáciách zásobníka protokolov. Na rozdiel od toho ekvivalentný nespravovaný kód môže mať jednoduchý ukazovateľ bez toho, aby vedel, či ukazuje na začiatok aktuálnej vyrovnávacej pamäte alebo na stred, a parameter dĺžky vyrovnávacej pamäte na určenie, kde je koniec spracovávaných údajov.

Aby ste predišli zbytočnému kopírovaniu pamäte, zaistite, aby bol posun a dĺžka akceptované vždy, keď dostanete bajtový parameter. Namiesto vlastnosti Dĺžka poľa použite parameter dĺžky a pridajte hodnotu posunu k aktuálnym indexom.

Náhodné čítanie a zlučovanie zápisu

Zlúčenie náhodného čítania a zápisu je funkcia podporovaná systémom Windows na čítanie do nesúvislých oblastí alebo zapisovanie údajov z nesúvislých oblastí, ako keby zaberali súvislú časť pamäte. Táto funkcia je poskytovaná vo Win32 API ako funkcie ReadFileScatter a WriteFileGather. Knižnica Windows Sockets podporuje aj náhodné čítanie a zlučovanie zápisov poskytovaním vlastných funkcií: WSASend, WSARecv a ďalšie.

Náhodné čítanie a zlučovací zápis môže byť užitočné v nasledujúcich situáciách:

    Keď má každý paket hlavičku pevnej veľkosti, ktorá predchádza skutočným údajom. Náhodné čítanie a zápis zlučovania vám umožní vyhnúť sa nutnosti kopírovať hlavičky zakaždým, keď potrebujete získať súvislú vyrovnávaciu pamäť.

    Keď je potrebné zbaviť sa dodatočnej réžie volaní systémových volaní pri vykonávaní I/O s viacerými vyrovnávacími pamäťami.

V porovnaní s funkciami ReadFileScatter a WriteFileGather, ktoré vyžadujú, aby každá vyrovnávacia pamäť mala presne veľkosť jednej stránky a aby sa rukoväť otvárala asynchrónne a bez vyrovnávacej pamäte (ešte väčšie obmedzenie), sa funkcie na čítanie a zápis zlučovania založené na zásuvkách javia praktickejšie. , pretože tieto obmedzenia nemajú. .NET Framework podporuje rozptylové čítanie a zlučovací zápis pre zásuvky prostredníctvom preťažených metód Socket.Send() a Socket.Receive() bez exportovania všeobecných funkcií čítania/zápisu.

Príklad použitia funkcií rozptylového čítania a zlučovania zápisu nájdete v triede HttpWebRequest. Spája hlavičky HTTP so skutočnými údajmi bez toho, aby sa uchýlil k vytvoreniu súvislej vyrovnávacej pamäte na ich uloženie.

Súbor I/O

Vstup a výstup súboru sa zvyčajne vykonáva prostredníctvom vyrovnávacej pamäte súborového systému, ktorá ponúka niekoľko výkonnostných výhod: ukladanie údajov, ku ktorým sa pristupovalo, do vyrovnávacej pamäte, čítanie dopredu (predbežné čítanie údajov z disku), pomalé zápisy (asynchrónne zápisy na disk) a zreťazenie zapisuje malé časti údajov. Po zobrazení výzvy systému Windows na očakávaný vzor prístupu k súborom môžete získať ďalšie zvýšenie výkonu. Ak vaša aplikácia vykonáva asynchrónne I/O a dokáže zvládnuť niektoré problémy s ukladaním do vyrovnávacej pamäte, lepším riešením môže byť úplné vyhýbanie sa mechanizmu ukladania do vyrovnávacej pamäte.

Správa vyrovnávacej pamäte

Pri vytváraní alebo otváraní súborov programátori odovzdávajú funkcie CreateFile príznaky a atribúty, z ktorých niektoré ovplyvňujú správanie mechanizmu ukladania do vyrovnávacej pamäte:

    Vlajka FILE_FLAG_SEQUENTIAL_SCAN označuje, že k súboru sa bude pristupovať sekvenčne, pričom možno preskočí časti a náhodný prístup je nepravdepodobný. Výsledkom je, že správca vyrovnávacej pamäte bude čítať dopredu a bude sa pozerať ďalej ako zvyčajne.

    Vlajka FILE_FLAG_RANDOM_ACCESS určuje, že k súboru sa bude pristupovať v náhodnom poradí. V tomto prípade bude správca vyrovnávacej pamäte vykonávať čítania s miernym predstihom, pretože je znížená šanca, že aplikácia bude skutočne potrebovať údaje načítané dopredu.

    Vlajka FILE_ATTRIBUTE_TEMPORARY označuje, že súbor je dočasný, takže skutočné operácie zápisu na fyzické médium (aby sa zabránilo strate údajov) môžu byť odložené.

V .NET sú tieto možnosti podporované (okrem poslednej) pomocou preťaženého konštruktora FileStream, ktorý preberá parameter typu enum FileOptions.

Náhodný prístup má negatívny vplyv na výkon, najmä pri práci s diskovými zariadeniami, pretože vyžaduje pohyb hláv. Ako sa technológia vyvíjala, priepustnosť disku sa zvýšila iba zvýšením hustoty úložiska, nie znížením latencie. Moderné disky sú schopné zmeniť poradie vykonávania dotazu s náhodným prístupom, aby sa skrátil celkový čas strávený pohybom hláv. Tento prístup sa nazýva radenie hardvérových príkazov (Native Command Queuing, NCO). Aby bola táto technika efektívnejšia, musí radič disku odoslať niekoľko I/O požiadaviek naraz. Inými slovami, ak je to možné, snažte sa mať naraz nevybavených viacero asynchrónnych I/O požiadaviek.

I/O bez vyrovnávacej pamäte

I/O operácie bez vyrovnávacej pamäte sa vždy vykonávajú bez použitia vyrovnávacej pamäte. Tento prístup má svoje výhody aj nevýhody. Rovnako ako v prípade triku na ovládanie vyrovnávacej pamäte, surové I/O sa povolia cez možnosť „príznaky a atribúty“ počas vytvárania súboru, ale .NET neposkytuje prístup k tejto schopnosti.

    Vlajka FILE_FLAG_NO_BUFFERING zakáže ukladanie čítania a zápisu do vyrovnávacej pamäte, ale neovplyvňuje ukladanie do vyrovnávacej pamäte vykonávané radičom disku. Vyhnete sa tak kopírovaniu (z vyrovnávacej pamäte používateľa do vyrovnávacej pamäte) a „znečisteniu“ vyrovnávacej pamäte (zapĺňaniu vyrovnávacej pamäte nepotrebnými údajmi a vytláčaniu potrebných). Čítania a zápisy bez vyrovnávacej pamäte však musia spĺňať požiadavky na zarovnanie.

    Nasledujúce parametre musia byť rovné alebo násobkom veľkosti sektora disku: veľkosť jedného prenosu, posun v súbore a adresa vyrovnávacej pamäte v pamäti. Typicky má diskový sektor veľkosť 512 bajtov. Najnovšie vysokokapacitné diskové zariadenia majú sektor s veľkosťou 4096 bajtov, ale môžu bežať v režime kompatibility, emulujúc 512-bajtové sektory (na úkor výkonu).

    Vlajka FILE_FLAG_WRITE_THROUGH povie správcovi vyrovnávacej pamäte, že má okamžite vyprázdniť údaje o zápise z vyrovnávacej pamäte (ak nie je nastavený príznak FILE_FLAG_NO_BUFFERING) a radiču disku oznámi, že má okamžite zapisovať na fyzické médium bez uloženia údajov do medziľahlej hardvérovej vyrovnávacej pamäte.

Čítanie dopredu zlepšuje výkon plnším využitím disku, aj keď aplikácia číta v synchrónnom režime s oneskoreniami medzi operáciami. Správne určenie, ktorú časť súboru si aplikácia vyžiada ako ďalšiu, je na systéme Windows. Vypnutím ukladania do vyrovnávacej pamäte zakážete aj čítanie dopredu a diskové zariadenie musíte zaneprázdniť vykonávaním viacerých prekrývajúcich sa I/O.

Zápisy s latenciou tiež zlepšujú výkon aplikácií, ktoré vykonávajú operácie synchrónneho zápisu tým, že vytvárajú ilúziu, že zápis na disk je veľmi rýchly. Aplikácia bude môcť zlepšiť využitie procesora blokovaním na kratšie časové obdobia. Ak je ukladanie do vyrovnávacej pamäte vypnuté, trvanie operácií zápisu sa bude rovnať celej dĺžke času potrebného na dokončenie zápisu údajov na disk. Preto je použitie asynchrónneho I/O režimu so zakázaným ukladaním do vyrovnávacej pamäte ešte dôležitejšie.

synchrónny model vstup výstup. Systémové volania read(2) , write(2) a ich ekvivalenty vracajú riadenie až po prečítaní alebo zápise údajov. Často to vedie k zablokovaniu vlákna.

Poznámka

V skutočnosti nie je všetko také jednoduché. read(2) by skutočne malo čakať, kým sa dáta fyzicky načítajú zo zariadenia, ale write(2) štandardne funguje v režime spätného zápisu: vráti sa po prenose údajov do vyrovnávacej pamäte systému, ale vo všeobecnosti pred údajmi sa fyzicky prenesie do zariadenia. To vo všeobecnosti výrazne zlepšuje pozorovaný výkon programu a umožňuje, aby sa dátová pamäť použila na iné účely ihneď po návrate write(2). Ale lenivé písanie má aj značné nevýhody. Tou hlavnou je, že výsledok fyzickej operácie nespoznáte okamžite podľa návratového kódu write(2) , ale až nejaký čas po návrate, zvyčajne podľa návratového kódu nasledujúceho volania write(2) . Pre niektoré aplikácie - pre transakčné monitory, pre mnohé programy v reálnom čase atď. - je to neprijateľné a sú nútené vypnúť lenivé písanie. Robí sa to pomocou príznaku O_SYNC, ktorý je možné nastaviť pri otvorení súboru a zmeniť na otvorenom súbore volaním fcntl(2) .

Synchronizáciu jednotlivých operácií zápisu je možné zabezpečiť volaním fsync(2) . Pre mnoho aplikácií, ktoré pracujú s viacerými zariadeniami a/alebo sieťovými pripojeniami synchrónny model nepríjemné. Práca v režime hlasovania tiež nie je vždy prijateľná. Je to preto, lebo select(3C) a poll(2) považujú deskriptor súboru za pripravený na čítanie až potom, čo sa dáta fyzicky objavia v jeho vyrovnávacej pamäti. Niektoré zariadenia však začnú poskytovať údaje až po výslovnom vyzvaní.

Pri niektorých aplikáciách, najmä pri aplikáciách v reálnom čase, je tiež dôležité poznať presný moment, kedy začnú prichádzať dáta. Pre takéto aplikácie môže byť tiež neprijateľné, aby výber(3C) a poll(2) zvažovali bežné súbory vždy pripravený na čítanie a písanie. naozaj, systém súborov sa číta z disku, a hoci je oveľa rýchlejší ako väčšina ostatných sieťové pripojenia, ale stále je prístup k nemu spojený s určitými oneskoreniami. Pre tvrdé aplikácie v reálnom čase nemusia byť tieto oneskorenia prijateľné - ale bez explicitnej požiadavky na čítanie systém súborov neposkytuje údaje!

Z pohľadu tvrdých real-time aplikácií môže byť významný ďalší aspekt I/O problému. Faktom je, že tvrdé aplikácie v reálnom čase majú vyššiu prioritu ako jadro, takže vykonávajú systémové volania – dokonca aj neblokujúce! - môže viesť k inverzia priority.

Riešenie týchto problémov je známe už dlho a nazýva sa asynchrónne I/O. V tomto režime sa I/O systémové volania vracajú hneď po požiadaní ovládača zariadenia, zvyčajne ešte pred skopírovaním údajov do systémovej vyrovnávacej pamäte. Vytvorenie požiadavky spočíva v umiestnení záznamu ( IRP , Input/Output Request Packet , Input/Output request paket) do fronty. Aby ste to dosiahli, stačí krátko zachytiť mutex, ktorý chráni "chvost" frontu, takže problém inverzie priority je ľahko prekonaný. Aby bolo možné zistiť, či sa hovor skončil, a ak skončil, ako presne a či je možné použiť pamäť, v ktorej boli dáta uložené, je k dispozícii špeciálne API (pozri obr. 8.1).


Ryža. 8.1.

Asynchrónny model bol hlavným I/O modelom v OS ako DEC RT-11, DEC RSX-11, VAX/VMS, OpenVMS. Takmer každý podporuje tento model v tej či onej forme. OS v reálnom čase. Od konca 80. rokov 20. storočia systémy Unix používali niekoľko nekompatibilných rozhraní API pre asynchrónne I/O. V roku 1993 ANSI/IEEE prijali POSIX 1003.1b, ktorý popisuje štandardizované API, ktoré preskúmame neskôr v tejto časti.

V Solaris 10 sú asynchrónne I/O funkcie zahrnuté v knižnici libaio.so. Ak chcete vytvoriť programy, ktoré používajú tieto funkcie, musíte použiť prepínač -laio. Funkcie aio_read(3AIO), aio_write(3AIO) a lio_listio(3AIO) sa používajú na generovanie požiadaviek na asynchrónne I/O.

Funkcie aio_read(3AIO) a aio_write(3AIO) majú jeden parameter, structaiocb *aiocbp . aiocb štruktúra definovaná v súbore< aio .h> a obsahuje nasledujúce polia:

  • int aio_fildes - deskriptor súboru
  • off_t aio_offset - offset v súbore, z ktorého sa má zapisovať alebo čítať
  • volatile void* aio_buf - vyrovnávacia pamäť na čítanie údajov alebo na zapisovanie údajov.
  • size_t aio_nbytes - veľkosť vyrovnávacej pamäte. Rovnako ako tradičné read(2) , aio_read(3AIO) môže čítať menej údajov, ako je požadované, ale nikdy čítať viac.
  • int aio_reqprio - požiadať o prioritu
  • struct sigevent aio_sigevent – ​​spôsob, ako signalizovať dokončenie požiadavky (diskutované neskôr v tejto časti)
  • int aio_lio_opcode - nepoužíva sa pre aio_read(3AIO) a aio_write(3AIO), používa sa iba funkciou lio_listio.

Funkcia lio_listio(3AIO) vám umožňuje generovať viacero I/O požiadaviek s jediným systémovým volaním. Táto funkcia má štyri parametre:

  • int režim - môže nadobúdať hodnoty LIO_WAIT (funkcia čaká na dokončenie všetkých požiadaviek) a LIO_NOWAIT (funkcia vráti riadenie ihneď po vytvorení všetkých požiadaviek).
  • struct aiocb *zoznam - zoznam ukazovateľov na štruktúry aiocb s popismi požiadaviek.

    Požiadavky možno čítať aj zapisovať, čo je určené poľom aio_lio_opcode. Požiadavky na jeden deskriptor sa vykonávajú v poradí, v akom sú špecifikované v poli zoznamu.

  • int nent - počet záznamov v poli zoznamu.
  • struct sigevent *sig - spôsob signalizácie dokončenia všetkých požiadaviek. Ak mode==LIO_WAIT , tento parameter sa ignoruje.

Knižnica POSIX AIO poskytuje dva spôsoby, ako oznámiť programu, že požiadavka bola dokončená, synchrónny a asynchrónny. Najprv sa pozrime na synchrónnu metódu. Funkcia aio_return(3AIO) vráti stav požiadavky. Ak je požiadavka už dokončená a úspešne dokončená, vráti veľkosť načítaných alebo zapísaných údajov v bajtoch. Podobne ako tradičné read(2) aj aio_return(3AIO) vracia 0 bajtov na konci súboru. Ak požiadavka zlyhala alebo ešte nebola dokončená, vráti sa -1 a nastaví sa errno. Ak požiadavka ešte nebola dokončená, kód chyby je EINPROGRESS .

aio_return(3AIO) je deštruktívny; ak sa zavolá na dokončenú požiadavku, zničí systémový objekt, ktorý obsahuje informácie o stave požiadavky. Volanie aio_return(3AIO) viackrát pre rovnakú požiadavku teda nie je možné.

Funkcia aio_error(3AIO) vráti kód chyby spojený s požiadavkou. Vráti 0, ak je požiadavka úspešná, kód chyby, ak zlyhá, alebo EINPROGRESS pre neúplné požiadavky.

Funkcia aio_suspend(3AIO) blokuje vlákno, kým sa nedokončí jedna z jeho špecifikovaných asynchrónnych I/O požiadaviek, alebo na určitý čas. Táto funkcia má tri parametre:

  • const struct aiocb *zoznam const- pole ukazovateľov na vyžiadanie deskriptorov.
  • int nent - počet prvkov v poli zoznamu.
  • const struct timespec *timeout- časový limit s presnosťou na nanosekundy (v skutočnosti s presnosťou na rozlíšenie systémový časovač).

Funkcia vráti 0, ak bola dokončená aspoň jedna z operácií uvedených v zozname. Ak funkcia zlyhá, vráti -1 a nastaví errno . Ak funkcia vypršala, vráti tiež -1 a errno==EINPROGRESS .

Príklad použitia asynchrónneho I/O s kontrolou stavu synchrónnej požiadavky je uvedený v príklade 8.3.

Const char req="GET / HTTP/1.0\r\n\r\n"; int main() ( int s; statická štruktúra aiocb readrq; statická štruktúra aiocb *readrqv=(&readrq, NULL); /* Otvorená zásuvka […] */ memset(&readrq, 0, sizeof readrq); readrq.aio_fildes=s ; readrq.aio_buf=buf; readrq.aio_nbytes=sizeof buf; if (aio_read(&readrq)) ( /* ... */ ) write(s, req, (sizeof req)-1); while(1) ( aio_suspend (readrqv , 1, NULL); size=aio_return(&readrq); if (size>0) (write(1, buf, size); aio_read(&readrq); ) else if (size==0) ( break; ) else if ( errno!=EINPROGRESS) ( perror("čítanie zo zásuvky"); ) ) ) 8.3. Asynchrónne I/O s kontrolou stavu synchrónnej požiadavky. Kód je skrátený, je z neho vylúčené otvorenie zásuvky a spracovanie chýb.

Asynchrónne oznámenie aplikácie o dokončení operácií spočíva v generovanie signálu na konci operácie. Ak to chcete urobiť, musíte vykonať príslušné nastavenia v poli aio_sigevent deskriptora požiadavky. Pole aio_sigevent je typu struct sigevent . Táto štruktúra je definovaná v a obsahuje nasledujúce polia:

  • int sigev_notify - režim upozornenia. Platné hodnoty sú SIGEV_NONE (neodosielať potvrdenia), SIGEV_SIGNAL (vygenerovať signál po dokončení požiadavky) a SIGEV_THREAD (po dokončení požiadavky spustiť zadanú funkciu v samostatnom vlákne). Solaris 10 podporuje aj typ výstrahy SIGEV_PORT, ktorý je uvedený v prílohe k tejto kapitole.
  • int sigev_signo je číslo signálu, ktoré sa vygeneruje pri použití SIGEV_SIGNAL .
  • union sigval sigev_value je parameter, ktorý sa má odovzdať obsluhe signálu alebo funkcii obsluhy. Pri použití pre asynchrónne I/O je to zvyčajne ukazovateľ na požiadavku.

    Pri použití SIGEV_PORT by to mal byť ukazovateľ na štruktúru port_event_t obsahujúcu číslo portu a prípadne ďalšie údaje.

  • void (*sigev_notify_function)(union sigval)- funkcia, ktorá sa bude volať pri použití SIGEV_THREAD .
  • pthread_attr_t *sigev_notify_attributes- atribúty vlákna, v ktorom sa spustí
  • sigev_notify_function pri použití SIGEV_THREAD .

Nie všetky implementácie libaio podporujú oznámenie SIGEV_THREAD. Niektoré systémy Unix namiesto toho používajú neštandardné oznámenie SIGEV_CALLBACK. Ďalej v tejto kapitole budeme diskutovať len o varovaní signálom.

Niektoré aplikácie používajú ako číslo signálu buď SIGIO alebo SIGPOLL (na Unix SVR4 je to rovnaký signál). Často sa používajú aj SIGUSR1 alebo SIGUSR2; je to výhodné, pretože to zabezpečuje, že sa podobný signál nevyskytne z iného dôvodu.

Aplikácie v reálnom čase tiež používajú čísla signálov v rozsahu SIGRTMIN až SIGRTMAX. Niektoré implementácie prideľujú na tento účel špeciálne číslo signálu SIGAIO alebo SIGASYNCIO, ale v systéme Solaris 10 takýto signál neexistuje.

Samozrejme, pred vykonaním asynchrónnych požiadaviek s upozornením na signál musíte nainštalovať obslužný program pre tento signál. Na upozornenie musíte použiť signály spracované v režime SA_SIGINFO. Nie je možné nastaviť takúto obsluhu pomocou systémových volaní signal(2) a sigset(2), musíte použiť sigaction(2) . Inštalácia ovládačov so sigakciou

Vstupné a výstupné operácie sú vo svojej podstate pomalšie ako iné typy spracovania. Toto spomalenie je spôsobené nasledujúcimi faktormi:

Oneskorenia spôsobené časom stráveným hľadaním požadovaných skladieb a sektorov na zariadeniach s náhodným prístupom (disky, CD).

Latencia spôsobená relatívne nízkou rýchlosťou prenosu údajov medzi fyzickými zariadeniami a systémovou pamäťou.

Oneskorenia prenosu údajov cez sieť pomocou súborov, serverov, dátových úložísk atď.

Vo všetkých predchádzajúcich príkladoch sa vykonávajú I/O operácie v synchronizácii s tokom takže celé vlákno je nútené bežať naprázdno, kým sa nedokončí.

Táto kapitola ukazuje, ako môžete zariadiť, aby vlákno pokračovalo vo vykonávaní bez čakania na dokončenie I/O, čo by bolo rovnaké ako vytváranie vlákien. asynchrónne vstup výstup. Rôzne techniky dostupné v systéme Windows sú ilustrované príkladmi.

Niektoré z týchto techník sa používajú v časovačoch čakania, ktoré sú tiež opísané v tejto kapitole.

Nakoniec, a čo je najdôležitejšie, učením sa o štandardných asynchrónnych I/O, ktoré môžeme použiť Porty na dokončenie I/O, ktoré sú mimoriadne užitočné pri budovaní škálovateľných serverov, ktoré dokážu podporovať veľký počet klientov bez toho, aby bolo potrebné pre každého z nich vytvárať samostatné vlákno. Program 14.4 je upravená verzia predtým vyvinutého servera, ktorý umožňuje použitie portov na dokončenie I/O.

Prehľad asynchrónnych I/O metód Windows

AT Spustenie systému Windows asynchrónne I/O sa poskytujú v súlade s tromi technikami.

Viacvláknový vstup/výstup (Multivláknové I/O). Každé z vlákien v rámci procesu alebo sady procesov vykonáva normálne synchrónne I/O, zatiaľ čo ostatné vlákna môžu pokračovať vo vykonávaní.

Prekrývajúce sa I/O. Po spustení čítania, zápisu alebo inej I/O operácie vlákno pokračuje vo svojom vykonávaní. Ak vlákno potrebuje výsledky I/O na pokračovanie vo vykonávaní, čaká, kým nebude k dispozícii príslušný popisovač alebo kým sa nevyskytne zadaná udalosť. V systéme Windows 9x sú prekrývajúce sa vstupy a výstupy podporované iba pre sériové zariadenia, ako napríklad pomenované kanály.

Rutiny dokončenia (rozšírené I/O) Keď sú I/O operácie dokončené, systém zavolá špeciálne postup dokončenia, beh vnútri vlákna. Rozšírený vstup/výstup pre súbory na disku nie je podporovaný v systéme Windows 9x.

Viacvláknové I/O využívajúce pomenované kanály je implementované vo viacvláknovom serveri, o ktorom sa hovorí v kapitole 11. Program grepMT (Program 7.1) riadi paralelné I/O operácie zahŕňajúce viacero súborov. Máme teda už množstvo programov, ktoré vykonávajú viacvláknové I/O a poskytujú tak formu asynchrónneho I/O.

Prekrývajúce sa I/O sú predmetom nasledujúcej časti a príklady konverzie súborov (z ASCII na UNICODE) v tejto časti používajú túto techniku ​​na ilustráciu možností sekvenčného spracovania súborov. Na tento účel sa používa upravená verzia programu 2.4. Po prekrývajúcich sa I/O sa uvažuje o rozšírených I/O pomocou dokončovacích rutín.

Poznámka

Prekrývajúce sa a rozšírené I/O metódy sa často ťažko implementujú, zriedka poskytujú nejaké výhody z hľadiska výkonu, niekedy dokonca spôsobujú zníženie výkonu a v prípade súborového I/O môžu fungovať iba pod Windows NT. Tieto problémy sú prekonané pomocou vlákien, tak veľa čitateľov bude pravdepodobne chcieť preskočiť na časti o čakacích časovačoch a portoch dokončenia I/O, podľa potreby sa vracať do tejto sekcie. Na druhej strane prvky asynchrónneho I/O sú prítomné v starých aj nových technológiách, a preto sa tieto metódy stále oplatí skúmať.

Napríklad technológia COM na platforme NT5 podporuje vyvolanie asynchrónnej metódy, takže táto technika môže byť užitočná pre mnohých čitateľov, ktorí používajú alebo plánujú používať technológiu COM. Taktiež operácie volania asynchrónnych procedúr (kapitola 10) majú veľa spoločného s rozšírenými I/O a hoci ja osobne radšej používam vlákna, iní môžu preferovať tento mechanizmus.

Prekrývajúce sa I/O

Prvá vec, ktorú musíte urobiť, aby ste zorganizovali asynchrónne I/O, či už prekrývajúce sa alebo rozšírené, je nastavenie atribútu overlapped na súbore alebo inom deskriptore. Ak to chcete urobiť, pri volaní CreateFile alebo inej funkcie, ktorá vedie k vytvoreniu súboru, pomenovaného kanála alebo iného ovládača, musíte zadať príznak FILE_FLAG_OVERLAPPED.

V prípade soketov (kapitola 12), či už vytvorených pomocou soketu alebo akceptovania, je atribút override štandardne nastavený vo Winsock 1.1, ale musí byť nastavený explicitne vo Winsock 2.0. Prekrývajúce sa zásuvky je možné použiť asynchrónne vo všetkých verziách systému Windows.

Až do tohto bodu sa štruktúry OVERLAPPED používali v spojení s funkciou LockFileEx a ako alternatíva k použitiu funkcie SetFilePointer (kapitola 3), ale sú tiež základným prvkom prekrývajúcich sa I/O. Tieto štruktúry fungujú ako voliteľné parametre pri volaní štyroch nižšie uvedených funkcií, ktoré sa môžu po dokončení operácií zablokovať.

Pripomeňme si, že keď zadáte príznak FILE_FLAG_OVERLAPPED ako súčasť parametra dwAttrsAndFlags (v prípade funkcie CreateFile) alebo parametra dwOpen-Mode (v prípade funkcie CreateNamedPipe), príslušný súbor alebo kanál možno použiť iba v prekrytých režim. Pri anonymných kanáloch prekrývajúce sa I/O nefungujú.

Poznámka

Dokumentácia pre funkciu CreateFile uvádza, že použitie príznaku FILE_FLAG_NO_BUFFERING zlepšuje výkon prekrývajúcich sa I/O. Experimenty ukazujú len okrajové zlepšenie výkonu (okolo 15 %, čo je možné overiť experimentovaním s programom 14.1), ale mali by ste sa uistiť, že celková veľkosť dát, ktoré sa čítajú počas operácie ReadFile alebo WriteFile, je násobkom sektora disku. veľkosť.

Prekrývajúce sa zásuvky

Jednou z najdôležitejších inovácií vo Windows Sockets 2.0 (kapitola 12) je štandardizácia prekrývajúcich sa I/O. Najmä sokety sa už automaticky nevytvárajú ako prekrývajúce sa deskriptory súborov. Funkcia socket vytvára neprekrývajúcu sa rukoväť. Ak chcete vytvoriť prekrývajúci sa soket, musíte zavolať funkciu WSASocket a explicitne požiadať o vytvorenie prekrývajúceho sa soketu zadaním hodnoty WSA_FLAG_OVERLAPPED pre parameter dwFlags funkcie WSASocket.

SOCKET WSAAPI WSASocket(int iAddressFamily, int iSocketType, int iProtocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags);

Ak chcete vytvoriť soket, namiesto funkcie soketu použite funkciu WSASocket. Každý soket vrátený funkciou accept bude mať rovnaké vlastnosti ako argument.

Dôsledky použitia prekrývajúcich sa I/O

Prekrývajúce sa I/O sa vykonávajú asynchrónne. To má niekoľko dôsledkov.

Prekrývajúce sa I/O operácie nie sú blokované. Funkcie ReadFile, WriteFile, TransactNamedPipe a ConnectNamedPipe sa vrátia bez čakania na dokončenie I/O operácie.

Návratovú hodnotu funkcie nemožno použiť ako kritérium pre úspech alebo zlyhanie jej vykonania, pretože vstupno-výstupná operácia v tomto bode ešte nemala čas na dokončenie. Označenie stavu prebiehajúceho I/O vyžaduje použitie iného mechanizmu.

Vrátená hodnota pre počet prenesených bajtov je tiež málo užitočná, pretože prenos údajov nemusí byť úplne dokončený. Ak chcete získať tento druh informácií, systém Windows musí poskytnúť iný mechanizmus.

Program sa môže opakovane pokúšať čítať alebo zapisovať pomocou rovnakého prekrývajúceho sa deskriptora súboru. Preto sa ukazovateľ súboru zodpovedajúci takémuto deskriptoru tiež ukazuje ako nevýznamný. Preto musí byť poskytnutá dodatočná metóda na poskytnutie pozície v súbore pre každú operáciu čítania alebo zápisu. V prípade pomenovaných kanálov to nie je problém vzhľadom na ich sekvenčnú povahu spracovania údajov.

Program musí byť schopný čakať (synchronizovať) na dokončenie I/O. Ak existuje viacero čakajúcich I/O operácií spojených s rovnakým handle, program musí byť schopný určiť, ktoré z operácií už boli dokončené. I/O operácie sa nemusia nevyhnutne dokončiť v rovnakom poradí, v akom sa začali vykonávať.

Na prekonanie posledných dvoch vyššie uvedených ťažkostí sa používajú OVERLAPPED štruktúry.

PREKRÝVAJÚCE sa štruktúry

Pomocou štruktúry OVERLAPPED (špecifikovanej napríklad parametrom lpOverlapped funkcie ReadFile) môžete zadať nasledujúce informácie:

Pozícia v súbore (64 bitov), ​​na ktorej by sa mala začať operácia čítania alebo zápisu, ako je uvedené v kapitole 3.

Udalosť (manuálne vymazaná), ktorá bude signalizovaná po dokončení súvisiacej operácie.

Nasleduje definícia OVERLAPPED štruktúry.

Polia Offset aj OffsetHigh by sa mali použiť na určenie pozície súboru (ukazovateľa), hoci horná časť ukazovateľa (OffsetHigh) je v mnohých prípadoch 0. Polia Internal a InternalHigh, ktoré sú vyhradené pre systémové použitie, by nemali byť použité.

Parameter hEvent je popisovač udalosti (vytvorený pomocou funkcie CreateEvent). Táto udalosť môže byť buď pomenovaná alebo nepomenovaná, ale to musieť byť povinné manuálne resetovateľné (pozri kapitolu 8), ak sa používa pre prekrývajúce sa I/O; dôvody pre to budú čoskoro vysvetlené. Keď sa I/O operácia dokončí, udalosť prejde do signalizovaného stavu.

V inom možnom prípade použitia je handle hEvent NULL; v tomto prípade môže program čakať na signalizáciu deskriptora súboru, ktorý môže fungovať aj ako synchronizačný objekt (pozri upozornenia nižšie). Systém používa stavy signálov deskriptora súboru na sledovanie dokončenia operácií, ak je deskriptor hEvent NULL, to znamená, že deskriptor súboru je v tomto prípade objekt synchronizácie.

Poznámka

Z dôvodu pohodlia budeme používať termín „súborová rukoväť“ v súvislosti s rukoväťami špecifikovanými pri volaní funkcií ReadFile, WriteFile atď. súbor.

Keď sa uskutoční volanie I/O funkcie, táto udalosť je systémom okamžite vymazaná (nastavená na nesignalizovaný stav). Po dokončení I/O operácie sa udalosť nastaví do signalizovaného stavu a zostane tam, kým ju nepoužije iná I/O operácia. Udalosť musí byť manuálne resetovateľná, ak na jej signalizáciu môže čakať viacero vlákien (hoci v našich príkladoch sa používa iba jedno vlákno) a nemusia čakať na dokončenie operácie.

Aj keď je deskriptor súboru synchrónny (teda vytvorený bez príznaku FILE_FLAG_OVERLAPPED), štruktúra OVERLAPPED môže slúžiť ako alternatíva k funkcii SetFilePointer na určenie pozície súboru. V tomto prípade návrat z volania do ReadFile alebo iného volania nenastane, kým sa nedokončí vstupno-výstupná operácia. Túto funkciu sme už použili v kapitole 3. Všimnite si tiež, že prebiehajúce I/O operácie sú jednoznačne identifikované kombináciou deskriptora súboru a zodpovedajúcej štruktúry PREKRÝVAJÚCE SA.

Nižšie sú uvedené niektoré upozornenia, ktoré je potrebné vziať do úvahy.

Vyhnite sa opätovnému použitiu OVERLAPPED štruktúry, kým sa ešte musí dokončiť príslušná vstupno-výstupná operácia, ak nejaká existuje.

Podobne sa vyhnite opätovnému použitiu udalosti špecifikovanej v štruktúre OVERLAPPED.

Ak existuje viacero čakajúcich požiadaviek, ktoré odkazujú na rovnaký prekrývajúci sa popisovač, na synchronizáciu použite namiesto popisovačov súborov popisovače udalostí.

Ak štruktúra OVERLAPPED alebo udalosť pôsobí ako automatická premenná v rámci bloku, uistite sa, že blok nemôže opustiť, kým nebude synchronizovaný s I/O operáciou. Aby ste predišli úniku zdrojov, je potrebné dbať na to, aby ste pred opustením bloku zatvorili rukoväť.

Prekrývajúce sa I/O stavy

Funkcie ReadFile a WriteFile, ako aj vyššie uvedené dve funkcie potrubia, sa okamžite vrátia, keď sa použijú na vykonávanie prekrývajúcich sa I/O operácií. Vo väčšine prípadov sa vstupno-výstupná operácia do tohto bodu nedokončí a návratová hodnota čítania/zápisu bude FALSE. Funkcia GetLastError v tejto situácii vráti ERROR_IO_PENDING.

Po čakaní, kým objekt synchronizácie (udalosť alebo možno deskriptor súboru) signalizuje dokončenie operácie, musíte zistiť, koľko bajtov bolo prenesených. Toto je hlavný účel funkcie GetOverlappedResult.

BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPWORD lpcbTransfer, BOOL bWait)

Indikácia konkrétnej I/O operácie je zabezpečená kombináciou rukoväte a OVERLAPPED štruktúry. Hodnota parametra bWait TRUE označuje, že funkcia GetOverlappedResult by mala počkať, kým sa operácia nedokončí; inak musí byť návrat z funkcie okamžitý. V oboch prípadoch táto funkcia vráti hodnotu TRUE až po úspešnom dokončení operácie. Ak je návratová hodnota funkcie GetOverlappedResult FALSE, potom funkcia GetLastError vráti ERROR_IO_INCOMPLETE, čo umožňuje volanie tejto funkcie na dokončenie I/O.

Počet prenesených bajtov je uložený v premennej *lpcbTransfer. Vždy sa uistite, že štruktúra OVERLAPPED zostala nezmenená od momentu jej použitia v prekrývajúcej sa I/O operácii.

Zrušenie prekrývajúcich sa I/O operácií

Booleovská funkcia CancelIO vám umožňuje zrušiť vykonávanie čakajúcich prekrývajúcich sa I/O operácií spojených so zadaným handle (táto funkcia má iba jeden parameter). Všetky operácie iniciované volajúcim vláknom pomocou tohto ovládača sú zrušené. Operácie iniciované inými vláknami nie sú ovplyvnené týmto volaním funkcie. Zrušené operácie sa končia chybou ERROR OPERATION ABORTED.

Príklad: Použitie deskriptora súboru ako synchronizačného objektu

Prekrývanie I/O je veľmi pohodlné a ľahko implementovateľné v prípadoch, keď môže existovať iba jedna čakajúca operácia. Potom na účely synchronizácie môže program použiť nie udalosť, ale deskriptor súboru.

Útržok kódu uvedený nižšie ukazuje, ako môže program spustiť operáciu čítania na prečítanie časti súboru, pokračovať v jeho vykonávaní, aby vykonal iné spracovanie, a potom vstúpiť do stavu čakania na signál deskriptora súboru.

OVERLAPPED ov = ( 0, 0, 0, 0, NULL /* Udalosti sa nepoužívajú. */ );
hF = CreateFile(…, FILE_FLAG_OVERLAPPED, …);
ReadFile(hF, Buffer, sizeof(Buffer), &nRead, &ov);
/* Vykonajte iné spracovanie. nRead nie je nevyhnutne platné.*/
/* Počkajte na dokončenie operácie čítania. */
WaitForSingleObject(hF, INFINITE);
GetOverlappedResult(hF, &ov, &nRead, FALSE);

Príklad: Konverzia súborov pomocou prekrývaných I/O a viacnásobného ukladania do vyrovnávacej pamäte

Program 2.4 (atou) konvertoval súbor ASCII na UNICODE sekvenčným spracovaním súboru a kapitola 5 ukázala, ako vykonať rovnaké sekvenčné spracovanie pomocou mapovania súborov. Program 14.1 (atouOV) rieši rovnaký problém pomocou prekrývajúcich sa I/O a viacerých vyrovnávacích pamätí, ktoré uchovávajú záznamy s pevnou veľkosťou.

Obrázok 14.1 znázorňuje organizáciu programu so štyrmi vyrovnávacími pamäťami s pevnou veľkosťou. Program je implementovaný tak, že počet vyrovnávacích pamätí je možné špecifikovať pomocou symbolickej konštanty preprocesora, ale v nasledujúcej diskusii budeme predpokladať, že sú vyrovnávacie pamäte štyri.

Najprv program inicializuje všetky prvky OVERLAPPED štruktúr, ktoré definujú udalosti a pozície v súboroch. Pre každú vstupnú a výstupnú vyrovnávaciu pamäť existuje samostatná štruktúra OVERLAPPED. Potom sa inicializuje prekrývajúca sa operácia čítania pre každú zo vstupných vyrovnávacích pamätí. Ďalej pomocou funkcie WaitForMultipleObjects program čaká na jednu udalosť označujúcu dokončenie čítania alebo zápisu. Po dokončení operácie čítania sa vstupná vyrovnávacia pamäť skopíruje a skonvertuje na zodpovedajúcu výstupnú vyrovnávaciu pamäť, po ktorej sa spustí operácia zápisu. Po dokončení zápisu sa spustí ďalšia operácia čítania. Všimnite si, že udalosti spojené so vstupnou a výstupnou vyrovnávacou pamäťou sú umiestnené v jednom poli, ktoré sa používa ako argument pri volaní funkcie WaitForMultipleObjects.

Ryža. 14.1. Asynchrónny model aktualizácie súborov


Program 14.1. atouOV: konverzia súborov pomocou prekrývajúcich sa I/O
Konverzia súboru z ASCII do Unicode pomocou prekrývajúcich sa I/O. Program funguje iba na Windows NT. */

#define MAX_OVRLP 4 /* Počet prekrývajúcich sa I/O.*/
#define REC_SIZE 0x8000 /* 32 KB: Minimálna veľkosť záznamu pre prijateľný výkon. */

/* Každý z prvkov premenných polí definovaných nižšie */
/* a štruktúry zodpovedajú jednej čakajúcej operácii */
/* prekrývajúce sa I/O. */
DWORD nin, nout, ic, i;
OVERLAPPED OverLapIn, OverLapOut;
/* Treba použiť pevné, dvojrozmerné pole */
/* diktované funkciou WaitForMultipleObjects. */
/* Hodnota 0 prvého indexu zodpovedá čítaniu, hodnota 1 zodpovedá zápisu. */
/* V každom z dvoch polí vyrovnávacej pamäte definovaných nižšie je prvý index */
/* číslo I/O operácie. */
LARGE_INTEGER CurPosIn, CurPosOut, FileSize;
/* Celkový počet záznamov na spracovanie, vypočítané */
/* na základe veľkosti vstupného súboru. Záznam na konci */
/* môže byť neúplný. */
pre (ic = 0; ic< MAX_OVRLP; ic++) {
/* Vytvorte udalosti čítania a zápisu pre každú PREKRÝVAJÚCU štruktúru.*/
hEvents = OverLapIn.hEvent /* Prečítanie udalosti.*/
hEvents = OverLapOut.hEvent /* Zápis udalosti. */
= CreateEvent(NULL, TRUE, FALSE, NULL);
/* Počiatočné pozície súboru pre každú PREKRÝVAJÚCU štruktúru. */
/* Spustí operáciu prekrývajúceho sa čítania na tejto štruktúre OVERLAPPED. */
if (CurPosIn.QuadPart< FileSize.QuadPart) ReadFile(hInputFile, AsRec, REC_SIZE, &nin, &OverLapIn);
/* Vykonajú sa všetky operácie čítania. Počkajte na dokončenie udalosti a okamžite ju resetujte. Udalosti čítania a zápisu sú uložené v poli udalostí vedľa seba. */
iWaits=0; /* Počet doteraz vykonaných I/O operácií. */
zatiaľ čo (iWaits< 2 * nRecord) {
ic = WaitForMultipleObjects(2 * MAX_OVRLP, hEvents, FALSE, INFINITE) - WAIT_OBJECT_0;
iWaits++; /* Zvýši počítadlo dokončených I/O operácií. */
ResetEvent(hEvents);
/* Čítanie dokončené. */
GetOverlappedResult(hInputFile, &OverLapIn, &nin, FALSE);
pre (i = 0; i< REC_SIZE; i++) UnRec[i] = AsRec[i];
WriteFile(hOutputFile, UnRec, nin * 2, &nout, &OverLapOut);
/* Pripravte sa na ďalšie čítanie, ktoré sa spustí po dokončení operácie zápisu spustenej vyššie. */
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;
) inak, ak (ic< 2 * MAX_OVRLP) { /* Операция записи завершилась. */
/* Začnite čítať. */
ic -= MAX_OVRLP; /* Nastaví index výstupnej vyrovnávacej pamäte. */
if (!GetOverlappedResult (hOutputFile, &OverLapOut, &nout, FALSE)) ReportError(_T("Chyba pri čítaní."), 0, TRUE);
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
if (CurPosIn.QuadPart< FileSize.QuadPart) {
/* Spustenie novej operácie čítania. */
ReadFile(hInputFile, AsRec, REC_SIZE, &nin, &OverLapIn);
/* Zatvorte všetky udalosti. */
pre (ic = 0; ic< MAX_OVRLP; ic++) {

Program 14.1 môže bežať len pod Windows NT. Asynchrónne I/O zariadenia Windows 9x neumožňujú použitie diskových súborov. V prílohe B sú uvedené výsledky a komentáre o relatívne slabom výkone programu atouOV. Experimenty ukázali, že veľkosť vyrovnávacej pamäte musí byť aspoň 32 KB, aby sa dosiahol prijateľný výkon, ale aj tak je normálny synchrónny I/O rýchlejší. Okrem toho sa výkon tohto programu nezlepší ani v podmienkach SMP, pretože v tomto príklade, v ktorom sa spracúvajú iba dva súbory, nie je CPU kritickým zdrojom.

Rozšírené I/O pomocou procedúry dokončenia

Existuje aj iný možný prístup k používaniu synchronizačných objektov. Namiesto toho, aby vlákno čakalo na signál ukončenia z udalosti alebo handle, systém môže zavolať definované užívateľom dokončovacie rutiny ihneď po dokončení I/O operácie. Procedúra ukončenia potom môže spustiť ďalšiu vstupno-výstupnú operáciu a vykonať potrebné zúčtovanie pre použitie systémových prostriedkov. Táto nepriamo volaná procedúra dokončenia (spätného volania) je podobná volaniu asynchrónnej procedúry použitej v kapitole 10 a vyžaduje použitie výstražných stavov čakania.

Ako možno v programe špecifikovať postup ukončenia? Medzi parametrami alebo dátovými štruktúrami funkcií ReadFile a WriteFile nezostali žiadne, ktoré by sa dali použiť na uloženie adresy procedúry ukončenia. Existuje však rodina rozšírených I/O funkcií, ktoré sú označené príponou "Ex" a obsahujú ďalší parameter na odovzdanie adresy ukončovacej rutiny. Funkcie čítania a zápisu sú ReadFileEx a WriteFileEx. Okrem toho je potrebná jedna z nasledujúcich pohotovostných funkcií.

Rozšírené I/O sa niekedy označujú ako clo vstup/výstup(výstražné I/O). Ako používať pokročilé funkcie je popísané v nasledujúcich častiach.

Poznámka

V systéme Windows 9x nemôžu rozšírené I/O pracovať s diskovými súbormi a komunikačnými portami. Pokročilý I/O Windows 9x je zároveň schopný pracovať s pomenovanými kanálmi, poštovými schránkami, zásuvkami a sériovými zariadeniami.

Funkcie ReadFileEx, WriteFileEx a postupy dokončovania

Rozšírené funkcie čítania a zápisu možno použiť v spojení s rukoväťami otvoreného súboru, pomenovaného kanála a poštovej schránky, ak bol príslušný objekt otvorený (vytvorený) s nastaveným príznakom FILE_FLAG_OVERLAPPED. Všimnite si, že tento príznak nastavuje atribút handle, a hoci sa prekrývajúce a rozšírené I/O líšia, rovnaký príznak platí pre oba typy asynchrónnych I/O úchytov.

Prekrývajúce sa zásuvky (kapitola 12) možno použiť s funkciami ReadFileEx a WriteFileEx vo všetkých verziách Windows.

BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpcr)
BOOL WriteFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpcr)

Obe funkcie už poznáte, až na to, že každá z nich má ďalší parameter, ktorý vám umožňuje špecifikovať adresu postupu dokončenia.

Každá z funkcií musí poskytovať OVERLAPPED štruktúru, ale nie je potrebné špecifikovať prvok hEvent tejto štruktúry; systém to ignoruje. Tento prvok je však veľmi užitočný na prenos informácií, ako je poradové číslo používané na rozlíšenie medzi jednotlivými I/O operáciami, ako je uvedené v programe 14.2.

Pri porovnaní s funkciami ReadFile a WriteFile môžete vidieť, že rozšírené funkcie nevyžadujú parametre na uloženie počtu prenesených bajtov. Tieto informácie sa odovzdávajú funkcii ukončenia, ktorá musí byť zahrnutá v programe.

Funkcia dokončenia poskytuje parametre pre počet bajtov, kód chyby a adresu štruktúry OVERLAPPED. Posledný z týchto parametrov je potrebný, aby postup dokončenia mohol určiť, ktorá z zostávajúcich operácií bola dokončená. Všimnite si, že predchádzajúce varovania o opätovnom použití alebo zničení OVERLAPPED štruktúr tu platia rovnako ako v prípade prekrývajúcich sa I/O.

ZRUŠIŤ WINAPI FileIOCompletionRoutine(DWORD dwError, DWORD cbTransferred, LPOVERLAPPED lpo)

Rovnako ako v prípade funkcie CreateThread, ktorá sa volá aj s názvom nejakej funkcie, name FileIOCompletionRoutine je zástupný symbol, nie skutočný názov procedúry ukončenia.

Hodnoty parametra dwError sú obmedzené na 0 (úspech) a ERROR_HANDLE_EOF (pri pokuse o čítanie mimo hraníc súboru). Štruktúra OVERLAPPED je tá, ktorú používa dokončené volanie ReadFileEx alebo WriteFileEx.

Predtým, ako systém zavolá procedúru ukončenia, musia sa stať dve veci:

1. Operácia I/O sa musí dokončiť.

2. Volajúce vlákno musí byť v ospalom stave a upozorniť systém, že potrebuje vykonať procedúru dokončenia vo fronte.

Ako vlákno prechádza do pohotovostného stavu čakania? Musí uskutočniť explicitné volanie jednej z funkcií strážneho psa opísaných v ďalšej časti. Vlákno teda vytvára podmienky, ktoré znemožňujú predčasné vykonanie procedúry ukončenia. Vlákno môže byť v ospalom stave len dovtedy, kým sa volá volanie funkcie ospalosti; po návrate tejto funkcie vlákno opustí zadaný stav.

Ak sú splnené obe tieto podmienky, vykonajú sa dokončovacie rutiny zaradené do frontu ako výsledok dokončenia I/O operácií. Rutiny dokončenia bežia na rovnakom vlákne, ktoré vykonalo pôvodné volanie I/O funkcie a je v stave nečinnosti. Preto by vlákno malo vstúpiť do pohotovostného stavu iba vtedy, keď existujú bezpečné podmienky na vykonanie procedúr ukončenia.

Pohotovostné funkcie

Pohotovostných funkcií je celkovo päť, ale nasledujúce sú prototypy iba troch z nich, ktoré nás priamo zaujímajú:

DWORD WaitForSingleObjectEx(HANDLE hObject, DWORD dwMilliseconds, BOOL bAlertable)
DWORD WaitForMultipleObjectsEx(DWORD cObjects, LPHANDLE lphObjects, BOOL fWaitAll, DWORD dwMilliseconds, BOOL bAlertable)
DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlerable)

Každá z funkcií strážneho psa má príznak bAlertable, ktorý musí byť nastavený na hodnotu TRUE v prípade asynchrónneho I/O. Vyššie uvedené funkcie sú rozšíreniami funkcií čakania a spánku, ktoré poznáte.

Trvanie čakacích intervalov sa uvádza ako obvykle v milisekundách. Každá z týchto troch funkcií sa vráti, akonáhle akýkoľvek z nasledujúcich situácií:

Rukoväť(y) prechádzajú do signalizovaného stavu, čím sú splnené štandardné požiadavky dvoch čakacích funkcií.

Časový limit vyprší.

Všetky procedúry dokončenia vo fronte vlákna sa prestanú vykonávať a bAlertable sa nastaví na hodnotu TRUE. Procedúra dokončenia sa zaradí do frontu po dokončení príslušnej I/O operácie (obrázok 14.2).

Všimnite si, že žiadne udalosti nie sú priradené k OVERLAPPED štruktúram vo funkciách ReadFileEx a WriteFileEx, takže žiadne rukoväte špecifikované pri volaní funkcie čakania nie sú priamo spojené so žiadnou konkrétnou I/O operáciou. Funkcia SleepEx zároveň nie je spojená so synchronizačnými objektmi, a preto je jej použitie najjednoduchšie. V prípade funkcie SleepEx je trvanie intervalu spánku zvyčajne nastavené na NEKONEČNÉ, takže návrat z tejto funkcie nastane až po dokončení jednej alebo viacerých procedúr dokončenia, ktoré sú momentálne vo fronte.

Vykonajte postup dokončenia a vráťte sa z pohotovostného režimu

Po dokončení rozšírenej I/O operácie sa jej pridružená rutina dokončenia s jej argumentmi špecifikujúcimi štruktúru OVERLAPPED, počet bajtov a chybový kód zaradí do frontu na vykonanie.

Všetky rutiny dokončenia vo fronte vlákna sa začnú vykonávať, keď vlákno vstúpi do stavu nečinnosti. Vykonávajú sa jeden po druhom, ale nie nevyhnutne v rovnakom poradí, v akom boli dokončené I/O operácie. Návrat z funkcie watchdog nastane až po vrátení procedúry dokončenia. Táto funkcia je dôležitá na zabezpečenie správneho fungovania väčšiny programov, pretože predpokladá, že ukončovacie rutiny dostanú príležitosť pripraviť sa na ďalšie použitie štruktúry OVERLAPPED a vykonať ďalšie potrebné akcie na uvedenie programu do známeho stavu pred návratom z režimu spánku. štát.

Ak je návrat z funkcie SleepEx spôsobený vykonaním jednej alebo viacerých procedúr dokončenia vo fronte, potom návratová hodnota funkcie bude WAIT_TO_COMPLETION a rovnakú hodnotu vráti funkcia GetLastError zavolaná po tom, čo jedna z čakacích funkcií vrátený.

Na záver si všimneme dva body:

1. Pri volaní ktorejkoľvek z funkcií watchdog použite ako hodnotu parametra intervalu čakania INFINITE. Ak neexistuje možnosť časového limitu, funkcie sa vrátia až po dokončení vykonávania všetkých rutín dokončenia alebo po prejdení deskriptorov do signalizovaného stavu.

2. Bežne sa používa dátový člen hEvent štruktúry OVERLAPPED na odovzdanie informácií do procedúry dokončenia, pretože OS toto pole ignoruje.

Interakcia medzi hlavným vláknom, dokončovacími rutinami a funkciami watchdog je znázornená na obr. 14.2. Tento príklad spustí tri súbežné čítania, z ktorých dve sú dokončené v čase, keď sa začne čakanie v pohotovostnom režime.

Ryža. 14.2. Asynchrónne I/O pomocou dokončovacích rutín

Príklad: Konverzia súboru pomocou rozšíreného I/O

Program 14.3 (atouEX) je revidovaná verzia programu 14.1. Tieto programy ilustrujú rozdiel medzi týmito dvoma metódami asynchrónneho I/O. atouEx je podobný programu 14.1, ale presunul veľkú časť kódu na sekvenovanie zdrojov do finalizátora a urobil mnoho premenných globálnymi, aby k nim mal finalizátor prístup. Príloha B však ukazuje, že z hľadiska výkonu je atouEx celkom konkurencieschopný s inými metódami, ktoré nepoužívajú mapovanie súborov, zatiaľ čo atouOV je pomalší.

Program 14.2. atouEx: konverzia súborov pomocou rozšírených I/O
Konverzia súboru z ASCII do Unicode pomocou EXTENDED I/O. */
/* súbor atouEX1 súbor2 */

#define REC_SIZE 8096 /* Veľkosť bloku nie je z hľadiska výkonu taká dôležitá ako pri atouOV. */
#define UREC_SIZE 2 * REC_SIZE

statické VOID WINAPI ReadDone(DWORD, DWORD, LPOVERLAPPED);
statické VOID WINAPI WriteDone(DWORD, DWORD, LPOVERLAPPED);

/* Prvá OVERLAPPED štruktúra je na čítanie a druhá na písanie. Štruktúry a vyrovnávacie pamäte sú pridelené pre každú nadchádzajúcu operáciu. */
OVERLAPPED OverLapIn, OverLapOut ;
CHAR AsRec;
WCHAR UnRec;
HANDLE hInputFile, hOutputFile;

int _tmain(int argc, LPTSTR argv) (
hInputFile = CreateFile(argv, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
hOutputFile = CreateFile(argv, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
FileSize.LowPart = GetFileSize(hInputFile, &FileSize.HighPart);
nRecord = FileSize.QuadPart / REC_SIZE;
if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;
pre (ic = 0; ic< MAX_OVRLP; ic++) {
OverLapIn.hEvent = (HANDLE)ic; /* Znovu načítajte udalosť. */
OverLapOut.hEvent = (HANDLE)ic; /* Polia. */
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;
if (CurPosIn.QuadPart< FileSize.QuadPart) ReadFileEx(hInputFile, AsRec, REC_SIZE, &OverLapIn , ReadDone);
CurPosIn.QuadPart += (LONGLONG)REC_SIZE;
/* Vykonajú sa všetky operácie čítania. Prejdite do pohotovostného stavu a zostaňte v ňom, kým sa nespracujú všetky záznamy.*/
kým (nHotovo< 2 * nRecord) SleepEx(INFINITE, TRUE);
_tprintf(_T("Konverzia ASCII do Unicode dokončená.\n"));

static VOID WINAPI ReadDone (kód DWORD, DWORD nBytes, LPOVERLAPPED pOv) (
/* Čítanie dokončené. Konvertujte dáta a spustite zápis. */
LARGE_INTEGER CurPosIn, CurPosOut;
/* Spracovať zápis a spustiť operáciu zápisu. */
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
CurPosOut.QuadPart = (CurPosIn.QuadPart / REC_SIZE) * UREC_SIZE;
OverLapOut.Offset = CurPosOut.LowPart;
OverLapOut.OffsetHigh = CurPosOut.HighPart;
/* Konvertovať záznam z ASCII na Unicode. */
pre (i = 0; i< nBytes; i++) UnRec[i] = AsRec[i];
WriteFileEx(hOutputFile, UnRec, nBytes*2, &OverLapOut, WriteDone);
/* Pripravte štruktúru OVERLAPPED na ďalšie čítanie. */
CurPosIn.QuadPart += REC_SIZE * (LONGLONG)(MAX_OVRLP);
OverLapIn.Offset = CurPosIn.LowPart;
OverLapIn.OffsetHigh = CurPosIn.HighPart;

static VOID WINAPI WriteHotovo (kód DWORD, DWORD nBytes, LPOVERLAPPED pOv) (
/* Zápis dokončený. Spustite ďalšiu operáciu čítania. */
CurPosIn.LowPart = OverLapIn.Offset;
CurPosIn.HighPart = OverLapIn.OffsetHigh;
if (CurPosIn.QuadPart< FileSize.QuadPart) {
ReadFileEx(hInputFile, AsRec, REC_SIZE, &OverLapIn, ReadDone);

Asynchrónne I/O pomocou viacerých vlákien

Prekrývajúce sa a rozšírené I/O umožňujú asynchrónne I/O v rámci jedného vlákna, hoci operačný systém vytvára svoje vlastné vlákna na podporu tejto funkcie. V tej či onej forme sa metódy tohto typu často používajú v mnohých skorých operačných systémoch na podporu obmedzených foriem vykonávania asynchrónnych operácií v jednovláknových systémoch.

Windows však poskytuje podporu multi-threadingu, takže je možné dosiahnuť rovnaký efekt vykonávaním synchrónnych I/O operácií na viacerých nezávisle vykonávaných vláknach. Tieto schopnosti už boli demonštrované skôr na viacvláknových serveroch a programe grepMT (kapitola 7). Okrem toho vlákna poskytujú koncepčne sekvenčný a údajne oveľa jednoduchší spôsob vykonávania asynchrónneho I/O. Ako alternatívu k metódam používaným v programoch 14.1 a 14.2 by sa dalo každému vláknu prideliť vlastný deskriptor súboru a potom by každé vlákno mohlo spracovať každý štvrtý záznam synchrónne.

Tento spôsob využitia streamov je demonštrovaný v programe atouMT, ktorý nie je súčasťou knihy, ale je súčasťou materiálu zverejneného na webovej stránke. AtouMT je nielen schopný bežať na akejkoľvek verzii Windowsu, ale je tiež jednoduchší ako ktorýkoľvek z dvoch asynchrónnych I/O programov, pretože účtovanie o využití zdrojov je menej komplikované. Každé vlákno jednoducho udržiava svoje vlastné vyrovnávacie pamäte na svojom vlastnom zásobníku a vykonáva sériu synchrónnych čítaní, transformácií a zápisov v slučke. Výkon programu zároveň zostáva na pomerne vysokej úrovni.

Poznámka

Program atouMT.c na webovej stránke komentuje niekoľko možných „úskalí“, s ktorými sa môžete stretnúť, keď umožníte viacerým vláknam pristupovať k rovnakému súboru súčasne. Najmä všetky jednotlivé popisovače súborov musia byť vytvorené pomocou funkcie CreateHandle, nie pomocou funkcie DuplicateHandle.

Osobne radšej používam viacvláknové spracovanie súborov ako asynchrónne I/O. Prúdy sa ľahšie programujú a vo väčšine prípadov poskytujú lepší výkon.

Existujú dve výnimky všeobecné pravidlo. Prvý z nich, ako je uvedené vyššie v tejto kapitole, sa zaoberá situáciami, v ktorých môže existovať iba jedna nevybavená operácia a na účely synchronizácie možno použiť deskriptor súboru. Druhá, dôležitejšia výnimka nastáva v prípade asynchrónnych portov na dokončenie I/O, o ktorých bude reč na konci tejto kapitoly.

Čakacie časovače

Windows NT podporuje čakateľné časovače, ktoré sú jedným typom objektu jadra, ktorý vykonáva čakanie.

Vždy si môžete vytvoriť svoj vlastný hodinový signál vytvorením hodinového vlákna, ktoré nastaví udalosť budenia po vyvolaní funkcie Sleep. V programe serverNP (Program 11.3) server tiež používa tok hodín na pravidelné vysielanie názvu svojho kanála. Čakacie časovače preto poskytujú trochu nadbytočný, ale pohodlný spôsob, ako organizovať úlohy tak, aby sa spúšťali pravidelne alebo podľa špecifického plánu. Predovšetkým môže byť časovač čakania nakonfigurovaný tak, aby sa signál generoval v presne definovanom čase.

Časovač čakania môže byť buď synchronizačný časovač, alebo manuálne resetovaný časovač upozornení. Časovač synchronizácie je spojený s funkciou nepriameho volania podobnou procedúre rozšíreného dokončenia I/O, zatiaľ čo funkcia čakania sa používa na synchronizáciu manuálneho resetovaného časovača upozornenia.

Prvým krokom je vytvorenie rukoväte časovača pomocou funkcie CreateWaitableTimer.

HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCTSTR lpTimerName);

Druhý parameter, bManualReset, určuje, či typ časovača, ktorý sa má vytvoriť, je synchronizovaný alebo oznamujúci. Program 14.3 používa časovač synchronizácie, ale zmenou nastavení komentárov a parametrov ho ľahko zmeníte na časovač upozornení. Všimnite si, že existuje aj funkcia OpenWaitableTimer, ktorá môže použiť voliteľný názov uvedený v treťom argumente.

Spočiatku je časovač vytvorený v neaktívnom stave, ale pomocou funkcie SetWaitableTimer ho môžete aktivovať a určiť počiatočné časové oneskorenie, ako aj trvanie časového intervalu medzi periodicky generovanými signálmi.

BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG IPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRutine, BOOL fResume);

hTimer je platný popisovač časovača vytvorený pomocou funkcie CreateWaitableTimer.

Druhý parameter, na ktorý ukazuje ukazovateľ pDueTime, môže nadobúdať buď kladné hodnoty zodpovedajúce absolútnemu času, alebo záporné hodnoty zodpovedajúce relatívnemu času, pričom skutočné hodnoty sú vyjadrené v časových jednotkách 100 nanosekúnd a ich formát je opísaný štruktúrou FILETIME. . Premenné typu FILETIME boli predstavené v kapitole 3 a boli použité už v kapitole 6 v programe timep (Program 6.2).

Hodnota intervalu medzi signálmi, špecifikovaná v treťom parametri, je vyjadrená v milisekundách. Ak je táto hodnota nastavená na 0, potom je časovač signalizovaný iba raz. Ak je tento parameter kladný, časovač je periodický a beží pravidelne, kým sa jeho činnosť neukončí volaním funkcie CancelWaitableTimer. Záporné hodnoty pre zadaný interval nie sú povolené.

Štvrtý parameter, pfnCompletionRoutine, sa používa v prípade synchronizačného časovača a špecifikuje adresu rutiny dokončenia, ktorá sa volá, keď je časovač signalizovaný. a poskytnutéže vlákno prejde do stavu nečinnosti. Pri volaní tejto procedúry je jedným z argumentov ukazovateľ špecifikovaný piatym parametrom plArgToComplretionRoutine.

Nastavením synchronizačného časovača môžete vlákno uviesť do ospalého stavu zavolaním funkcie SleepEx, čím sa zabezpečí, že bude možné zavolať procedúru ukončenia. V prípade manuálneho resetovania upozorňujúceho časovača by ste mali počkať, kým nebude signalizovaná rukoväť časovača. Rukoväť zostane v signalizovanom stave až do ďalšieho volania funkcie SetWaitableTimer. Plná verzia programu 14.3, ktorú nájdete na webovej stránke, vám umožňuje spúšťať vlastné experimenty pomocou časovača podľa vášho výberu v kombinácii s procedúrou ukončenia alebo čakaním na signál rukoväte časovača, výsledkom čoho sú štyri rôzne kombinácie.

Posledný parameter, fResume, súvisí s režimami úspory energie. Ďalšie informácie o tejto téme nájdete v dokumentácii pomocníka.

Funkcia CancelWaitableTimer sa používa na zrušenie predtým nazývanej funkcie SetWaitableTimer, ale nemení signalizovaný stav časovača. Ak to chcete urobiť, musíte znova zavolať funkciu SetWaitableTimer.

Príklad: Použitie časovača čakania

Program 14.3 demonštruje použitie časovača spánku na generovanie periodických signálov.

Program 14.3. TimeBeep: generovanie periodických signálov
/* Kapitola 14. TimeBeep.p. Pravidelné zvukové upozornenie. */
/* Použitie: perióda TimeBeep (v milisekundách). */

statický BOOL WINAPI Handler(DWORD CntrlEvent);
statický VOID APIENTRY Beeper(LPVOID, DWORD, DWORD);
volatile static BOOL Exit = FALSE;

int _tmain(int argc, LPTSTR argv) (
/* Zachytenie stlačenia klávesov ukončiť operáciu. Pozri kapitolu 4. */
SetConsoleCtrlHandler(Handler, TRUE);
DueTime.QuadPart = -(LONGLONG)Period * 10000;
/* Parameter DueTime je záporný počas prvého časového limitu a je relatívny k aktuálnemu času. Časový limit sa meria v ms (10 -3 s), zatiaľ čo DueTime sa meria v jednotkách 100 ns (10 -7 s), aby bol konzistentný s typom FILETIME. */
hTimer = CreateWaitableTimer(NULL, FALSE /* "Časovač synchronizácie" */, NULL);
SetWaitableTimer(hTimer, &DueTime, Period, Beeper, &Count, TRUE);
_tprintf(_T("Pocet = %d\n"), Pocet);
/* Hodnota počítadla sa zvýši v procedúre časovača. */
/* Zadajte pohotovostný režim. */
_tprintf(_T("Dokončenie. Počítadlo = %d"), Počet);

static VOID APIENTRY Beeper(LPVOID lpCount, DWORD dwTimerLowValue, DWORD dwTimerHighValue) (
*(LPDWORD)lpCount = *(LPDWORD)lpCount + 1;
_tprintf(_T("Generovať číslo signálu: %d\n"), *(LPDWORD) lpCount);
Fan(1000 /* Frekvencia. */, 250 /* Trvanie (ms). */);

BOOL WINAPI Handler (DWORD CntrlEvent) (
_tprintf(_T("Vypnutie\n"));

Aplikačný programátor nemusí myslieť na také veci, ako napríklad ako systémové programy pracujú s registrami zariadení. Systém pred aplikáciami skrýva detaily nízkoúrovňovej práce so zariadeniami. Rozdiel medzi pollingom a interrupt I/O sa však do určitej miery prejavuje na úrovni systémových funkcií, v podobe funkcií pre synchrónne a asynchrónne I/O.

Vykonávanie funkcie synchrónne I/O zahŕňa spustenie I/O operácie a čakanie na dokončenie tejto operácie. Až po dokončení I/O funkcia vráti riadenie volajúcemu programu.

Synchrónne I/O je pre programátorov najznámejší spôsob práce so zariadeniami. Štandardné vstupné/výstupné rutiny programovacích jazykov fungujú týmto spôsobom.

Volanie funkcie asynchrónne I/O znamená len začiatok príslušnej operácie. Potom funkcia okamžite vráti riadenie volajúcemu programu bez čakania na dokončenie operácie.

Zvážte napríklad asynchrónne zadávanie údajov. Je jasné, že program nemôže pristupovať k údajom, kým si nie je istý, že jeho zadávanie bolo dokončené. Ale je celkom možné, že program môže zatiaľ robiť inú prácu a nezaháľať v očakávaní.

Skôr či neskôr musí program predsa len začať pracovať so zadanými údajmi, najskôr sa však uistite, že asynchrónna operácia už skončila. Rôzne operačné systémy na to poskytujú nástroje, ktoré možno rozdeliť do troch skupín.

· Čakanie na dokončenie operácie. Je to ako „druhá polovica synchrónnej operácie“. Program najprv spustil operáciu, potom vykonal nejaké vedľajšie akcie a teraz čaká na koniec operácie, ako pri synchrónnom vstupe / výstupe.

· Kontrola dokončenia operácie. V tomto prípade program nečaká, ale iba kontroluje stav asynchrónnej operácie. Ak vstup/výstup ešte nie je dokončený, program má možnosť prejsť sa ešte nejaký čas.

· Postup pri vymenovaní výpovede. V tomto prípade pri spustení asynchrónnej operácie užívateľský program oznámi systému adresu užívateľom definovanej procedúry alebo funkcie, ktorú by mal systém zavolať po dokončení operácie. Samotný program už nemusí zaujímať priebeh I/O, systém mu to v správnom čase pripomenie zavolaním zadanej funkcie. Táto metóda je najflexibilnejšia, pretože používateľ môže poskytnúť akékoľvek akcie v procese dokončovania.

V aplikácii Windows sú dostupné všetky tri metódy na dokončenie asynchrónnych operácií. UNIX nemá asynchrónne I/O funkcie, ale rovnaký asynchrónny efekt možno dosiahnuť iným spôsobom, spustením dodatočného procesu.

Asynchrónne vykonávanie I/O umožňuje v niektorých prípadoch zvýšiť výkon a poskytnúť ďalšie funkčnosť. Bez takej jednoduchej formy asynchrónneho vstupu, akým je „vstup z klávesnice bez čakania“, by nebolo možné realizovať množstvo počítačových hier a simulátorov. Logika programu využívajúceho asynchrónne operácie je zároveň komplikovanejšia ako pri synchrónnych operáciách.

A aký je vyššie uvedený vzťah medzi synchrónnymi / asynchrónnymi operáciami a metódami I / O diskutovanými v predchádzajúcom odseku? Na túto otázku si odpovedzte sami.

I/O manažment.

blokovo orientované zariadenia a bajtovo orientovaný

Hlavná myšlienka

kľúč princíp je nezávislosť zariadenia

Manipulácia s prerušením,

ovládače zariadení,

Zdá sa zrejmé, že z rôznych dôvodov je možná široká škála prerušení. Preto je s prerušením spojené číslo – takzvané číslo prerušenia.

Toto číslo jednoznačne zodpovedá konkrétnej udalosti. Systém je schopný rozpoznať prerušenia a keď nastanú, spustí procedúru zodpovedajúcu číslu prerušenia.

Niektoré prerušenia (prvých päť v číselnom poradí) sú rezervované na použitie CPU v prípade akýchkoľvek špeciálnych udalostí, ako je pokus o delenie nulou, pretečenie atď. (v skutočnosti ide o J interné prerušenia).

Hardvérové ​​prerušenia sa vždy vyskytujú asynchrónne s ohľadom na spustené programy. Okrem toho môže nastať niekoľko prerušení súčasne!

Aby sa systém nestratil pri rozhodovaní o tom, ktoré prerušenie obsluhovať ako prvé, existuje špeciálna schéma priorít. Každé prerušenie má priradenú vlastnú prioritu. Ak dôjde k viacerým prerušeniam súčasne, systém uprednostní najvyššiu prioritu a ostatné prerušenia na chvíľu odloží.

Systém priority je implementovaný na dvoch čipoch Intel 8259 (alebo podobných). Každý čip je radičom prerušení a spravuje až osem priorít. Čipy je možné kombinovať (kaskádovať), aby sa zvýšil počet úrovní priority v systéme.

Úrovne priority sú označované skratkou IRQ0 - IRQ15.


24. Manažment vstupov / výstupov. Synchrónne a asynchrónne I/O.

Jednou z hlavných funkcií operačného systému je správa všetkých vstupných / výstupných zariadení počítača. OS musí posielať príkazy zariadeniam, zachytávať prerušenia a spracovávať chyby; musí tiež poskytovať rozhranie medzi zariadeniami a zvyškom systému. Na účely vývoja by rozhranie malo byť rovnaké pre všetky typy zariadení (nezávislosť na zariadení). Viac o otázke kontroly I/O 23.

Zásady ochrany

Keďže operačný systém UNIX bol od svojho počiatku koncipovaný ako viacužívateľský operačný systém, problém autorizácie prístupu rôznych používateľov k súborom súborového systému bol v ňom vždy aktuálny. Autorizáciou prístupu rozumieme akcie systému, ktoré povolia alebo zakážu prístup. tohto používateľa k tomuto súboru, v závislosti od prístupových práv používateľa a obmedzení prístupu nastavených pre daný súbor. Schéma oprávnenia prístupu používaná v operačnom systéme UNIX je taká jednoduchá a pohodlná a zároveň taká výkonná, že sa stala de facto štandardom moderných operačných systémov (ktoré sa nevydávajú za systémy s viacúrovňovou ochranou).

Ochrana súborov

Ako je vo viacužívateľskom operačnom systéme zvykom, UNIX zachováva jednotný mechanizmus riadenia prístupu k súborom a adresárom súborového systému. Akýkoľvek proces môže pristupovať k určitému súboru vtedy a len vtedy, ak prístupové práva opísané pri súbore zodpovedajú možnostiam tento proces.

Ochrana súborov pred neoprávneným prístupom v systéme UNIX je založená na troch faktoch. Po prvé, každý proces, ktorý vytvára súbor (alebo adresár), je spojený s nejakým systémovým jedinečným identifikátorom používateľa (UID - Identifikátor používateľa), ktorý možno ďalej považovať za identifikátor vlastníka novovytvoreného súboru. Po druhé, každý proces, ktorý sa pokúša o prístup k súboru, má priradený pár identifikátorov, aktuálneho používateľa a identifikátory skupiny. Po tretie, každý súbor jednoznačne zodpovedá svojmu deskriptoru - i-uzlu.

Posledná skutočnosť stojí za to podrobnejšie sa zaoberať. Je dôležité pochopiť, že názvy súborov a súbory ako také nie sú to isté. Najmä ak existuje viacero pevných odkazov na rovnaký súbor, viaceré názvy súborov v skutočnosti predstavujú rovnaký súbor a sú spojené s rovnakým i-uzlom. Akýkoľvek i-uzol použitý v súborovom systéme vždy jednoznačne zodpovedá jednému a iba jednému súboru. I-uzol obsahuje pomerne veľa rôznych informácií (väčšina z nich je dostupná používateľom prostredníctvom systémových volaní stat a fstat) a medzi týmito informáciami je aj časť, ktorá umožňuje súborovému systému vyhodnotiť prístupové práva daného procesu. do daného súboru v požadovanom režime.

Všeobecné princípy ochrany sú rovnaké pre všetky existujúce varianty systému: Informácie o i-uzle zahŕňajú UID a GID aktuálneho vlastníka súboru (ihneď po vytvorení súboru sú identifikátory jeho aktuálneho vlastníka nastavené na zodpovedajúci aktuálny identifikátor procesu tvorcu, ale môže byť neskôr zmenený systémovými volaniami chown a chgrp) . Okrem toho i-uzol súboru obsahuje stupnicu, ktorá udáva, čo môže používateľ – jeho vlastník so súborom robiť, čo môžu so súborom robiť používatelia patriaci do rovnakej skupiny používateľov ako vlastník a čo môžu robiť iní. používateľov súboru. Malé detaily implementácie v rôznych verziách systému sa líšia.

28. Kontrola prístupu k súborom v systéme Windows NT. Zoznamy prístupových práv.

Systém riadenia prístupu vo Windows NT je iný vysoký stupeň flexibilita, ktorá sa dosahuje vďaka širokej škále prístupových subjektov a objektov, ako aj granularite prístupových operácií.

Kontrola prístupu k súborom

Pre zdieľané prostriedky používa systém Windows NT spoločný objektový model, ktorý obsahuje také bezpečnostné charakteristiky, ako je množina povolených operácií, identifikátor vlastníka a zoznam riadenia prístupu.

Objekty vo Windows NT sa vytvárajú pre akýkoľvek prostriedok v prípade, že sú zdieľané alebo sa stanú zdieľanými – súbory, adresáre, zariadenia, časti pamäte, procesy. Charakteristiky objektov vo Windows NT sú rozdelené na dve časti – všeobecnú časť, ktorej zloženie nezávisí od typu objektu, a individuálnu časť, určenú typom objektu.
Všetky objekty sú uložené v stromových hierarchických štruktúrach, ktorých prvkami sú vetvové objekty (adresáre) a listové objekty (súbory). Pre objekty súborového systému je táto schéma vzťahov priamym odrazom hierarchie adresárov a súborov. Pre objekty iného typu má hierarchická schéma vzťahov svoj vlastný obsah, napríklad pri procesoch odráža vzťahy rodič-dieťa a pri zariadeniach príslušnosť k určitému typu zariadenia a prepojenie zariadenia s inými zariadeniami, napr. napríklad radič SCSI s diskami.

Kontrola prístupových práv pre objekty akéhokoľvek typu sa vykonáva centrálne pomocou Security Reference Monitor, ktorý beží v privilegovanom režime.

Bezpečnostný systém Windows NT sa vyznačuje prítomnosťou veľkého množstva rôznych preddefinovaných (zabudovaných) prístupových subjektov – individuálnych používateľov aj skupín. Takže v systéme sú vždy takí používatelia ako správca, systém a hosť, ako aj skupiny používateľov, správcovia, prevádzkovatelia účtov, prevádzkovatelia serverov, všetci a iní. Zmyslom týchto vstavaných používateľov a skupín je, že majú pridelené určité práva, čo administrátorom uľahčuje vytváranie efektívny systém Riadenie prístupu. Pri pridávaní nového používateľa musí administrátor len rozhodnúť, do ktorej skupiny alebo skupín tohto používateľa priradí. Správca môže samozrejme vytvárať nové skupiny a tiež pridávať práva do vstavaných skupín na implementáciu vlastnej bezpečnostnej politiky, ale v mnohých prípadoch stačia vstavané skupiny.

Windows NT podporuje tri triedy operácií prístupu, ktoré sa líšia typom subjektov a objektov zapojených do týchto operácií.

□ Oprávnenia sú množinou operácií, ktoré je možné definovať pre subjekty všetkých typov s ohľadom na objekty akéhokoľvek typu: súbory, adresáre, tlačiarne, pamäťové časti atď. Oprávnenia svojím účelom zodpovedajú prístupovým právam k súborom a adresárom v systéme QC UNIX.

□ Práva (používateľské práva) - sú definované pre subjekty typu skupiny na vykonávanie niektorých systémových operácií: nastavenie systémového času, archivácia súborov, vypnutie počítača a pod. Tieto operácie zahŕňajú špeciálny prístupový objekt - operačný systém ako celok .

Sú to predovšetkým práva, nie povolenia, ktoré odlišujú jednu vstavanú skupinu používateľov od druhej. Niektoré práva vstavanej skupiny sú tiež vstavané - nemožno ich z tejto skupiny odstrániť. Zostávajúce práva vstavanej skupiny je možné odstrániť (alebo pridať zo všeobecného zoznamu práv).

□ Používateľské schopnosti sú definované pre jednotlivých používateľov na vykonávanie akcií súvisiacich s tvorbou ich operačného prostredia, napríklad zmena zloženia hlavnej ponuky programu, možnosť používať položku ponuky Spustiť atď. Znížením množiny možností ( ktoré sú používateľovi štandardne dostupné), môže správca prinútiť používateľa pracovať s operačným prostredím, ktoré správca považuje za najvhodnejšie a chráni používateľa pred prípadnými chybami.

Práva a povolenia udelené skupine sú automaticky udelené jej členom, čo umožňuje správcovi zaobchádzať s veľkým počtom používateľov ako s jednotkou informácií o účte a minimalizovať ich činnosti.

Keď sa používateľ prihlási do systému, vytvorí sa mu takzvaný prístupový token, ktorý obsahuje ID používateľa a ID všetkých skupín, do ktorých používateľ patrí. Token tiež obsahuje: predvolený zoznam riadenia prístupu (ACL), ktorý pozostáva z povolení a vzťahuje sa na objekty vytvorené procesom; zoznam používateľských práv na vykonávanie systémových akcií.

Všetky objekty, vrátane súborov, tokov, udalostí, dokonca aj prístupových tokenov, sú pri vytváraní vybavené popisovačom zabezpečenia. Popisovač zabezpečenia obsahuje zoznam riadenia prístupu - ACL.

Deskriptor súboru je nezáporné celé číslo priradené OS k súboru otvorenému procesom.

ACL(Angličtina) Zoznam riadenia prístupu- zoznam kontroly prístupu, v angličtine vyslovované ako „ekl“) – určuje, kto alebo čo môže pristupovať ku konkrétnemu objektu a aké operácie smie alebo zakáže tento subjekt vykonávať na objekte.

Zoznamy kontroly prístupu sú chrbticou systémov selektívnej kontroly prístupu. ( Wiki)

Vlastník objektu, zvyčajne používateľ, ktorý ho vytvoril, má právo selektívne riadiť prístup k objektu a môže zmeniť ACL objektu tak, aby umožnil alebo nepovolil ostatným prístup k objektu. Vstavaný správca Windows NT, na rozdiel od superužívateľa UNIX, nemusí mať nejaké povolenia na prístup k objektu. Na implementáciu tejto funkcie môžu byť ID administrátorov a ID skupín administrátorov zahrnuté do ACL, rovnako ako bežné ID užívateľov. Správca má však stále možnosť vykonávať akékoľvek operácie s akýmikoľvek objektmi, pretože sa môže kedykoľvek stať vlastníkom objektu a potom ako vlastník získať celý súbor oprávnení. Získať však späť držanie predchádzajúci majiteľ objekt, správca nemôže, takže používateľ môže vždy zistiť, že správca pracoval s jeho súborom alebo tlačiarňou.

Keď proces požaduje operáciu na prístup k objektu vo Windows NT, riadenie sa vždy prenesie na bezpečnostný monitor, ktorý porovná ID užívateľa a skupiny užívateľov z prístupového tokenu s ID uloženými v prvkoch ACL objektu. Na rozdiel od UNIXu môžu prvky Windows NT ACL obsahovať zoznamy povolených operácií a zoznamy zakázaných operácií pre používateľa.

Windows NT explicitne definuje pravidlá, podľa ktorých sa novovytvorenému objektu priradí ACL. Ak volací kód počas vytvárania objektu explicitne nastaví všetky prístupové práva k novovytvorenému objektu, potom bezpečnostný systém priradí tento ACL k objektu.

Ak volací kód neposkytuje objektu ACL, ale objekt má názov, potom platí princíp dedenia povolení. Bezpečnostný systém sa pozrie na ACL adresára objektov, kde je uložený názov nového objektu. Niektoré položky ACL adresára objektov môžu byť označené ako zdedené. To znamená, že môžu byť priradené k novým objektom vytvoreným v tomto adresári.

V prípade, že proces explicitne nenastavil ACL pre vytváraný objekt a objekt adresára nemá žiadne zdedené prvky ACL, použije sa predvolený ACL z prístupového tokenu procesu.


29. Programovací jazyk Java. virtuálny stroj Java. technológia Java.

Java je objektovo orientovaný programovací jazyk vyvinutý spoločnosťou Sun Microsystems. Java aplikácie sú zvyčajne kompilované do špeciálneho bajtkódu, takže môžu bežať na akomkoľvek virtuálnom stroji Java (JVM) bez ohľadu na architektúru počítača. Java programy sú preložené do bajtkódu, ktorý spúšťa virtuálny stroj Java ( JVM) - program, ktorý spracováva bajtový kód a prenáša pokyny do zariadenia ako interpret, ale s tým rozdielom, že bajtový kód sa na rozdiel od textu spracováva oveľa rýchlejšie.

Výhodou tohto spôsobu vykonávania programov je úplná nezávislosť bajtkódu od operačného systému a hardvéru, čo umožňuje spúšťať Java aplikácie na akomkoľvek zariadení, pre ktoré existuje zodpovedajúci virtuálny stroj. Ďalšou dôležitou vlastnosťou technológie Java je flexibilné zabezpečenie vďaka tomu, že vykonávanie programu je plne riadené virtuálnym strojom. Akákoľvek operácia, ktorá prekročí nastavené oprávnenia programu (napríklad pokus o neoprávnený prístup k údajom alebo pripojenie k inému počítaču), spôsobí okamžité prerušenie.

Medzi nevýhody konceptu virtuálneho stroja často patrí skutočnosť, že vykonávanie bajtkódu virtuálnym strojom môže znížiť výkon programov a algoritmov implementovaných v jazyku Java.

Java Virtual Machine(skrátene Java VM, JVM) - virtuálny stroj Java - hlavná časť runtime systému Java, takzvané Java Runtime Environment (JRE). Java Virtual Machine interpretuje a spúšťa bajtový kód Java, ktorý bol predtým vygenerovaný zo zdrojového kódu programu Java kompilátorom Java (javac). JVM možno použiť aj na spúšťanie programov napísaných v iných programovacích jazykoch. Napríklad zdrojový kód Ada možno skompilovať do bajtkódu Java, ktorý potom môže JVM spustiť.

JVM je kľúčovým komponentom platformy Java. Keďže virtuálne stroje Java sú dostupné pre mnoho hardvérových a softvérových platforiem, Java môže byť vnímaná ako middleware aj samostatná platforma, teda princíp „zapíš raz, spusti kdekoľvek“. Použitie jedného bajtového kódu pre mnoho platforiem umožňuje Javu opísať ako „skompilovať raz, spustiť kdekoľvek“ (skompilovať raz, spustiť kdekoľvek).

Beh programu

Programy určené na spustenie na JVM musia byť skompilované v štandardizovanom prenosnom počítači binárny formát, ktorý je zvyčajne reprezentovaný ako súbory .class. Program môže pozostávať z mnohých tried umiestnených v rôznych súboroch. Na uľahčenie hosťovania veľkých programov môžu byť niektoré súbory .class zbalené spolu do takzvaného súboru .jar (skratka pre Java Archive).

JVM spúšťa súbory .class alebo .jar, pričom emuluje inštrukcie napísané pre JVM interpretáciou alebo použitím kompilátora just-in-time (JIT), akým je napríklad HotSpot od Sun microsystems. V súčasnosti sa kompilácia JIT používa vo väčšine JVM, aby sa dosiahla vyššia rýchlosť.

Ako väčšina virtuálnych strojov, aj Java Virtual Machine má architektúru orientovanú na zásobník, ktorá je spoločná pre mikrokontroléry a mikroprocesory.

JVM, čo je inštancia prostredia JRE (Java Runtime Environment), vstupuje do hry pri spúšťaní programov Java. Po dokončení vykonávania túto inštanciu odstráni zberač odpadu. JIT je súčasťou Java Virtual Machine, ktorá sa používa na zrýchlenie času vykonávania aplikácií. JIT súčasne kompiluje časti bajtového kódu, ktoré majú podobnú funkčnosť, a preto skracuje čas potrebný na kompiláciu.

j2se (štandardné vydanie Java 2) - Štandardná knižnica obsahuje:

GUI, NET, databáza…


30. Platforma .NET. Základné myšlienky a ustanovenia. .NET programovacie jazyky.

.Internetová sieť- softvérová technológia od spoločnosti Microsoft, určená na vytváranie bežných programov a webových aplikácií.

Jednou z hlavných myšlienok Microsoft .NET je kompatibilita rôznych služieb napísaných v rôznych jazykoch. Napríklad služba napísaná v C++ pre Microsoft .NET môže pristupovať k metóde triedy z knižnice napísanej v Delphi; v C# môžete napísať triedu, ktorá dedí z triedy napísanej v Visual Basic.NET a výnimku vyvolanú metódou napísanou v C# možno zachytiť a spracovať v Delphi. Každá knižnica (zostava) v .NET má informácie o svojej verzii, čo umožňuje eliminovať prípadné konflikty medzi rôzne verzie zhromaždenia.

Aplikácie je možné vyvíjať aj v textovom editore a používať konzolový kompilátor.

Podobne ako technológia Java, aj vývojové prostredie .NET vytvára bajtový kód, ktorý má spustiť virtuálny stroj. Vstupný jazyk tohto stroja v .NET sa nazýva MSIL (Microsoft Intermediate Language), alebo CIL (Common Intermediate Language, neskôr), alebo jednoducho IL.

Použitie bajtkódu vám umožňuje získať multiplatformový prístup na úrovni skompilovaného projektu (v podmienkach .NET: zhromaždenie), a to nielen na zdrojovej úrovni, ako napríklad v C. Pred spustením zostavy v runtime CLR je bajtový kód konvertovaný kompilátorom JIT zabudovaným do prostredia (just in time, kompilácia za behu) na strojové kódy cieľového procesora. Je tiež možné zostaviť zostavu do natívneho kódu pre zvolenú platformu pomocou utility NGen.exe dodávanej s .NET Framework.

Počas procesu prekladu je zdrojový kód programu (napísaný v SML, C #, Visual Basic, C ++ alebo v akomkoľvek inom programovacom jazyku, ktorý podporuje .NET) konvertovaný kompilátorom na takzvanú zostavu ( Assembly) a uloží sa ako dynamicky prepojený súbor knižnice (Dynamically Linked Library, DLL) alebo spustiteľný súbor (Executable, EXE).

Prirodzene, pre každý kompilátor (či už je to kompilátor jazyka C#, csc.exe alebo Visual Basic, vbc.exe) vykonáva runtime prostredie potrebné mapovanie typov používaných na typy CTS a programový kód na kód "abstraktný stroj" .NET - MSIL (Microsoft Intermediate Language).

Výsledkom je, že softvérový projekt je vytvorený ako zostava - sebestačný komponent pre nasadenie, replikáciu a opätovné použitie. Zostava je identifikovaná digitálnym podpisom autora a jedinečné číslo verzií.

Vstavané programovacie jazyky (dodávané s .NET Framework):

C#; J#; VB.NET JScript .NET C++/CLI - nová verzia C++ (Managed).


31. Funkčné komponenty OS. Správa súborov

Funkčné komponenty OS:

Funkcie operačného systému samostatného počítača sú zvyčajne zoskupené buď podľa typov lokálnych zdrojov, ktoré OS spravuje, alebo podľa špecifických úloh, ktoré sa vzťahujú na všetky zdroje. Niekedy sa takéto skupiny funkcií nazývajú podsystémy. Najdôležitejšie podsystémy správy prostriedkov sú podsystémy správy procesov, pamäte, súborov a externých zariadení, zatiaľ čo podsystémy spoločné pre všetky prostriedky sú používateľské rozhranie, ochrana údajov a podsystémy správy.

Správa súborov:

Schopnosť OS „chrániť“ zložitosti skutočného hardvéru sa veľmi jasne prejavuje v jednom z hlavných podsystémov OS – súborovom systéme.

Súborový systém spája pamäťové médium na jednej strane a API (Application Programming Interface) na prístup k súborom na strane druhej. Keď aplikačný program pristupuje k súboru, netuší, ako sa informácie nachádzajú v konkrétnom súbore, ani na akom fyzickom type média (CD, pevný disk, magnetická páska alebo flash pamäťová jednotka) sú zaznamenané. Všetko, čo program pozná, je názov súboru, jeho veľkosť a atribúty. Tieto údaje prijíma z ovládača súborového systému. Je to súborový systém, ktorý určuje, kde a ako bude súbor zapísaný na fyzické médium (napríklad pevný disk).

Z pohľadu operačného systému je celý disk súborom klastrov s veľkosťou 512 bajtov a viac. Ovládače súborového systému organizujú klastre do súborov a adresárov (čo sú vlastne súbory obsahujúce zoznam súborov v danom adresári). Tie isté ovládače sledujú, ktoré klastre sa momentálne používajú, ktoré sú voľné a ktoré sú označené ako neúspešné.

Súborový systém však nemusí byť nevyhnutne priamo spojený s fyzickým pamäťovým médiom. Existujú virtuálne súborové systémy, ako aj sieťové súborové systémy, ktoré sú len spôsobom prístupu k súborom, ktoré sú na vzdialenom počítači.

V najjednoduchšom prípade sú všetky súbory na danom disku uložené v jednom adresári. Táto jednoúrovňová schéma bola použitá v CP/M a v prvej verzii MS-DOS 1.0. Hierarchický súborový systém s vnorenými adresármi sa prvýkrát objavil v Multics, potom v UNIXe.

Adresáre na rôznych jednotkách môžu tvoriť niekoľko samostatných stromov, ako v DOS/Windows, alebo ich možno spojiť do jedného stromu spoločného pre všetky jednotky, ako v systémoch podobných UNIX.

V skutočnosti v systémoch DOS / Windows, ako aj v systémoch podobných UNIX, existuje jeden koreňový adresár s vnorenými adresármi s názvom „c:“, „d:“ atď. Do týchto adresárov sú pripojené oddiely pevného disku. To znamená, že c:\ je len odkaz na file:///c:/. Na rozdiel od súborových systémov podobných UNIXu však Windows zakazuje zapisovanie do koreňového adresára, ako aj prezeranie jeho obsahu.

V systéme UNIX existuje iba jeden koreňový adresár a všetky ostatné súbory a adresáre sú v ňom vnorené. Ak chcete získať prístup k súborom a adresárom na jednotke, musíte túto jednotku pripojiť pomocou príkazu mount. Napríklad, ak chcete otvoriť súbory na CD, jednoducho povedané, musíte povedať operačnému systému: "vezmite súborový systém na tomto CD a ukážte ho v adresári /mnt/cdrom." Všetky súbory a adresáre na CD sa objavia v tomto adresári /mnt/cdrom, ktorý sa nazýva bod pripojenia. Vo väčšine systémov podobných UNIX sú vymeniteľné disky (diskety a CD), flash disky a iné externé úložné zariadenia pripojené v adresári /mnt, /mount alebo /media. Operačné systémy Unix a UNIX podobné vám tiež umožňujú automaticky pripájať disky pri zavádzaní operačného systému.

Všimnite si použitie lomítok v operačných systémoch Windows, UNIX a UNIX (Windows používa opačnú lomku „\“, zatiaľ čo operačné systémy UNIX a UNIX používajú jednoduchú lomku „/“)

Okrem toho je potrebné poznamenať, že vyššie uvedený systém umožňuje pripojiť nielen súborové systémy fyzických zariadení, ale aj jednotlivé adresáre (parameter --bind) alebo napr. iso obraz(možnosť slučky). Doplnky ako FUSE tiež umožňujú pripojiť napríklad celý adresár na FTP a veľmi veľké množstvo rôznych zdrojov.

V NTFS a HFS sa používa ešte zložitejšia štruktúra. V týchto súborových systémoch je každý súbor súborom atribútov. Za atribúty sa nepovažuje len tradičný systém len na čítanie, ale aj názov súboru, veľkosť a dokonca aj obsah. Pre NTFS a HFS je teda to, čo je uložené v súbore, len jedným z jeho atribútov.

Podľa tejto logiky môže jeden súbor obsahovať viacero variácií obsahu. V jednom súbore tak môžu byť uložené viaceré verzie toho istého dokumentu, ako aj ďalšie údaje (ikona súboru, program priradený k súboru). Táto organizácia je typická pre HFS na počítačoch Macintosh.


32. Funkčné komponenty OS. Riadenie procesov.

Riadenie procesov:

Najdôležitejšou časťou operačného systému, ktorá priamo ovplyvňuje fungovanie počítača, je podsystém riadenia procesov. Proces (alebo inými slovami úloha) je abstrakcia, ktorá popisuje bežiaci program. Pre operačný systém je proces jednotkou práce, požiadavka na spotrebu systémových prostriedkov.

V multitaskingovom (viacprocesovom) systéme môže byť proces v jednom z troch základných stavov:

VYKONANIE - aktívny stav procesu, počas ktorého má proces k dispozícii všetky potrebné zdroje a je priamo vykonávaný procesorom;

WAITING - pasívny stav procesu, proces je zablokovaný, nemôže byť vykonaný z vlastných vnútorných dôvodov, čaká na výskyt nejakej udalosti, napríklad na dokončenie I/O operácie, prijatie správy od iného proces, uvoľnenie určitého zdroja, ktorý potrebuje;

PRIPRAVENÝ – tiež pasívny stav procesu, ale v tomto prípade je proces zablokovaný v dôsledku vonkajších okolností: proces má všetky potrebné zdroje, je pripravený na spustenie, ale procesor je zaneprázdnený vykonávaním iného procesu.

Počas životného cyklu sa každý proces pohybuje z jedného stavu do druhého v súlade s algoritmom plánovania procesov implementovaným v tomto operačnom systéme.

CP/M štandard

Začiatok tvorby operačných systémov pre mikropočítače položil OS SR/M. Bol vyvinutý v roku 1974, potom bol nainštalovaný na mnohých 8-bitových strojoch. V rámci tohto operačného systému vzniklo značné množstvo softvéru vrátane prekladačov textov BASIC, Pascal, C, Fortran, Cobol, Lisp, Ada a mnohých ďalších. Umožňujú pripraviť dokumenty oveľa rýchlejšie a pohodlnejšie ako s písacím strojom.

štandard MSX

Tento štandard určoval nielen OS, ale aj vlastnosti hardvéru pre školské počítače. Podľa normy MSX mal stroj mať RAM kapacita aspoň 16 K, pamäť len na čítanie 32 K so vstavaným tlmočníkom jazyka BASIC, farebný grafický displej s rozlíšením 256x192 pixelov a 16 farbami, trojkanálový generátor zvuku pre 8 oktáv, paralelný port na pripojenie tlačiarne a ovládač na ovládanie externe pripojeného externého disku.

Operačný systém takéhoto stroja by mal mať tieto vlastnosti: požadovaná pamäť - nie viac ako 16 K, kompatibilita s CP / M na úrovni systémových volaní, kompatibilita s DOSom z hľadiska formátov súborov na externých jednotkách založených na disketách, podpora pre prekladateľov BASIC, C, Fortran a Lisp.

Pi - systém

V počiatočnom období vývoja osobných počítačov vznikol operačný systém USCD p-system. Základom tohto systému bol takzvaný P-machine – program, ktorý emuluje hypotetický univerzálny počítač. P-stroj simuluje činnosť procesora, pamäte a externých zariadení vykonávaním špeciálnych inštrukcií nazývaných P-kód. Softvérové ​​komponenty Pi-systémy (vrátane kompilátorov) sú kompilované v P-kóde, aplikačné programy sú tiež kompilované do P-kódu. Hlavným rozlišovacím znakom systému teda bola minimálna závislosť od vlastností hardvéru PC. To zabezpečilo prenosnosť systému Pi odlišné typy stroje. Kompaktnosť P-kódu a vhodne implementovaný mechanizmus stránkovania umožnili spúšťať relatívne veľké programy na PC s malou RAM.

I/O manažment.

I/O zariadenia sú rozdelené do dvoch typov: blokovo orientované zariadenia a bajtovo orientovaný zariadení. Blokovo orientované zariadenia ukladajú informácie do blokov pevnej veľkosti, z ktorých každý má svoju vlastnú adresu. Najbežnejším blokovo orientovaným zariadením je disk. Zariadenia orientované na bajty nie sú adresovateľné a neumožňujú operáciu vyhľadávania, generujú alebo spotrebúvajú sekvenciu bajtov. Príkladmi sú terminály, riadkové tlačiarne, sieťové adaptéry. Elektronický komponent sa nazýva ovládač zariadenia alebo adaptér. Operačný systém sa zaoberá ovládačom. Regulátor vykonáva jednoduché funkcie, monitoruje a opravuje chyby. Každý radič má niekoľko registrov, ktoré sa používajú na interakciu s centrálnym procesorom. OS vykonáva I/O zápisom príkazov do registrov radiča. Disketový radič IBM PC akceptuje 15 príkazov ako READ, WRITE, SEEK, FORMAT atď. Po prijatí príkazu procesor opustí ovládač a vykoná inú prácu. Po dokončení príkazu radič zorganizuje prerušenie, aby preniesol kontrolu nad procesorom na operačný systém, ktorý musí skontrolovať výsledky operácie. Procesor získava výsledky a stav zariadenia načítaním informácií z registrov regulátora.

Hlavná myšlienka Organizácia I/O softvéru spočíva v jeho rozdelení do niekoľkých úrovní, pričom spodné úrovne poskytujú tienenie hardvérových funkcií od vyšších a tie poskytujú pohodlné rozhranie pre používateľov.

kľúč princíp je nezávislosť zariadenia. Forma programu by nemala závisieť od toho, či načítava dáta z diskety alebo z pevného disku. Ďalšou dôležitou otázkou pre softvér I/O je spracovanie chýb. Vo všeobecnosti by sa chyby mali riešiť čo najbližšie k hardvéru. Ak riadiaca jednotka zistí chybu čítania, musí sa ju pokúsiť opraviť. Ak zlyhá, ovládač zariadenia by mal opraviť chyby. A iba ak spodná úroveň nedokáže zvládnuť chybu, nahlási chybu vyššej úrovni.

Ďalšou kľúčovou otázkou je použitie blokujúcich (synchrónnych) a neblokujúcich (asynchrónnych) prenosov. Väčšina fyzických I/O operácií je asynchrónna – procesor spustí prenos a prejde na inú úlohu, kým nedôjde k prerušeniu. I/O operácie musia byť blokované - po príkaze READ sa program automaticky pozastaví, kým dáta nevstúpia do vyrovnávacej pamäte programu.

Posledným problémom je, že niektoré zariadenia sú zdieľané (disky: súčasný prístup viacerých používateľov na disk nie je problém), zatiaľ čo iné sú vyhradené (tlačiarne: nemôžete miešať riadky vytlačené rôznymi používateľmi).

Na vyriešenie vzniknutých problémov je vhodné rozdeliť I/O softvér do štyroch vrstiev (obrázok 2.30):

Manipulácia s prerušením,

ovládače zariadení,

Vrstva operačného systému nezávislá od zariadenia,

· Vlastná softvérová vrstva.

Koncept hardvérového prerušenia a jeho spracovanie.

Asynchrónne alebo externé (hardvérové) prerušenia - udalosti, ktoré pochádzajú z externých zdrojov (napr. periférií) a môže sa vyskytnúť v ľubovoľnom okamihu: signál z časovača, internetová karta alebo disková jednotka, stlačenie klávesov na klávesnici, pohyb myši; Vyžadujú okamžitú reakciu (spracovanie).

Takmer všetky I/O systémy v počítači fungujú pomocou prerušení. Najmä keď stlačíte klávesy alebo kliknete myšou, hardvér generuje prerušenia. V reakcii na ne systém prečíta kód stlačenej klávesy alebo si zapamätá súradnice kurzora myši. Prerušenia generuje radič disku, LAN adaptér, sériové porty, audio adaptér a ďalšie zariadenia.