Delen via


I/O van asynchrone schijf wordt als synchroon weergegeven in Windows

Dit artikel helpt u bij het oplossen van het probleem waarbij het standaardgedrag voor I/O synchroon is, maar het wordt weergegeven als asynchroon.

Oorspronkelijke productversie: Windows
Origineel KB-nummer: 156932

Samenvatting

Bestands-I/O in Microsoft Windows kan synchroon of asynchroon zijn. Het standaardgedrag voor I/O is synchroon, waarbij een I/O-functie wordt aangeroepen en wordt geretourneerd wanneer de I/O is voltooid. Met asynchrone I/O kan een I/O-functie de uitvoering onmiddellijk terugzetten naar de aanroeper, maar de I/O wordt pas in de toekomst als voltooid beschouwd. Het besturingssysteem meldt de aanroeper wanneer de I/O is voltooid. In plaats daarvan kan de aanroeper de status van de openstaande I/O-bewerking bepalen met behulp van services van het besturingssysteem.

Het voordeel van asynchrone I/O is dat de aanroeper tijd heeft om ander werk uit te voeren of meer aanvragen uit te geven terwijl de I/O-bewerking wordt voltooid. De term Overlapped I/O wordt vaak gebruikt voor asynchrone I/O en niet-overlappende I/O voor synchrone I/O. In dit artikel worden de termen Asynchroon en Synchroon gebruikt voor I/O-bewerkingen. In dit artikel wordt ervan uitgegaan dat de lezer bekend is met de bestands-I/O-functies, zoals CreateFile, ReadFile, WriteFile.

Asynchrone I/O-bewerkingen gedragen zich vaak net als synchrone I/O-bewerkingen. Bepaalde voorwaarden die in dit artikel in de latere secties worden besproken, waardoor de I/O-bewerkingen synchroon worden voltooid. De aanroeper heeft geen tijd voor achtergrondwerk omdat de I/O-functies pas terugkeren als de I/O is voltooid.

Verschillende functies zijn gerelateerd aan synchrone en asynchrone I/O. In dit artikel wordt en WriteFile als voorbeeld gebruiktReadFile. Goede alternatieven zijn ReadFileEx en WriteFileEx. Hoewel in dit artikel specifiek alleen schijf-I/O wordt besproken, kunnen veel van de principes worden toegepast op andere typen I/O, zoals seriële I/O of netwerk-I/O.

Asynchrone I/O instellen

De FILE_FLAG_OVERLAPPED vlag moet worden opgegeven in CreateFile wanneer het bestand wordt geopend. Met deze vlag kunnen I/O-bewerkingen op het bestand asynchroon worden uitgevoerd. Hier volgt een voorbeeld:

HANDLE hFile;

hFile = CreateFile(szFileName,
                      GENERIC_READ,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      FILE_FLAG_NORMAL | FILE_FLAG_OVERLAPPED,
                      NULL);

if (hFile == INVALID_HANDLE_VALUE)
      ErrorOpeningFile();

Wees voorzichtig bij het coden voor asynchrone I/O, omdat het systeem zich het recht voorbehoudt om een bewerking synchroon te maken als dat nodig is. Het is dus het beste als u het programma schrijft om een I/O-bewerking die synchroon of asynchroon kan worden uitgevoerd, correct af te handelen. De voorbeeldcode laat deze overweging zien.

Er zijn veel dingen die een programma kan doen terwijl u wacht tot asynchrone bewerkingen zijn voltooid, zoals het in de wachtrij plaatsen van extra bewerkingen of het uitvoeren van achtergrondwerk. De volgende code verwerkt bijvoorbeeld op de juiste manier overlappende en niet-overlappende voltooiing van een leesbewerking. Het doet niets meer dan wachten tot de openstaande I/O is voltooid:

if (!ReadFile(hFile,
               pDataBuf,
               dwSizeOfBuffer,
               &NumberOfBytesRead,
               &osReadOperation )
{
   if (GetLastError() != ERROR_IO_PENDING)
   {
      // Some other error occurred while reading the file.
      ErrorReadingFile();
      ExitProcess(0);
   }
   else
      // Operation has been queued and
      // will complete in the future.
      fOverlapped = TRUE;
}
else
   // Operation has completed immediately.
   fOverlapped = FALSE;

if (fOverlapped)
{
   // Wait for the operation to complete before continuing.
   // You could do some background work if you wanted to.
   if (GetOverlappedResult( hFile,
                           &osReadOperation,
                           &NumberOfBytesTransferred,
                           TRUE))
      ReadHasCompleted(NumberOfBytesTransferred);
   else
      // Operation has completed, but it failed.
      ErrorReadingFile();
}
else
   ReadHasCompleted(NumberOfBytesRead);

Opmerking

&NumberOfBytesRead doorgegeven aan ReadFile verschilt van &NumberOfBytesTransferred doorgegeven aan GetOverlappedResult. Als een bewerking asynchroon is gemaakt, wordt gebruikt GetOverlappedResult om het werkelijke aantal bytes te bepalen dat in de bewerking is overgedragen nadat deze is voltooid. De &NumberOfBytesRead doorgegeven aan ReadFile is zinloos.

Als een bewerking daarentegen onmiddellijk wordt voltooid, &NumberOfBytesRead is doorgegeven aan ReadFile geldig voor het aantal gelezen bytes. In dit geval negeert u de OVERLAPPED structuur die is doorgegeven aan ReadFile; gebruik deze niet met GetOverlappedResult of WaitForSingleObject.

Een ander voorbehoud bij asynchrone bewerking is dat u een OVERLAPPED structuur pas mag gebruiken als de in behandeling zijnde bewerking is voltooid. Met andere woorden, als u drie uitstekende I/O-bewerkingen hebt, moet u drie OVERLAPPED structuren gebruiken. Als u een OVERLAPPED structuur opnieuw gebruikt, ontvangt u onvoorspelbare resultaten in de I/O-bewerkingen en kan er sprake zijn van beschadiging van gegevens. Bovendien moet u deze correct initialiseren, zodat er geen gegevens overblijven van invloed zijn op de nieuwe bewerking voordat u een OVERLAPPED structuur voor het eerst kunt gebruiken of voordat u deze opnieuw gebruikt nadat een eerdere bewerking is voltooid.

Hetzelfde type beperking is van toepassing op de gegevensbuffer die in een bewerking wordt gebruikt. Een gegevensbuffer mag pas worden gelezen of geschreven als de bijbehorende I/O-bewerking is voltooid; het lezen of schrijven van de buffer kan fouten en beschadigde gegevens veroorzaken.

Asynchrone I/O lijkt nog steeds synchroon te zijn

Als u de instructies eerder in dit artikel hebt gevolgd, worden al uw I/O-bewerkingen echter nog steeds synchroon uitgevoerd in de opgegeven volgorde en geen van de ReadFile bewerkingen retourneert FALSE met GetLastError() retourneren ERROR_IO_PENDING, wat betekent dat u geen tijd hebt voor achtergrondwerk. Waarom gebeurt dit?

Er zijn een aantal redenen waarom I/O-bewerkingen synchroon worden uitgevoerd, zelfs als u hebt gecodeerd voor asynchrone bewerkingen.

Compressie

Een obstakel voor asynchrone bewerking is NTFS-compressie (New Technology File System). Het bestandssysteemstuurprogramma heeft geen asynchrone toegang tot gecomprimeerde bestanden; in plaats hiervan worden alle bewerkingen synchroon gemaakt. Deze obstructie is niet van toepassing op bestanden die zijn gecomprimeerd met hulpprogramma's die vergelijkbaar zijn met COMPRESS of PKZIP.

NTFS-versleuteling

Net als bij compressie zorgt bestandsversleuteling ervoor dat het systeemstuurprogramma asynchrone I/O converteert naar synchroon. Als de bestanden worden ontsleuteld, zijn de I/O-aanvragen asynchroon.

Een bestand uitbreiden

Een andere reden dat I/O-bewerkingen synchroon worden uitgevoerd, is de bewerkingen zelf. In Windows wordt elke schrijfbewerking naar een bestand dat de lengte ervan verlengt, synchroon uitgevoerd.

Opmerking

Toepassingen kunnen de eerder genoemde schrijfbewerking asynchroon maken door de geldige gegevenslengte van het bestand te wijzigen met behulp van de SetFileValidData functie en vervolgens een WriteFileuit te geven .

Met behulp SetFileValidData van (dat beschikbaar is in Windows XP en latere versies) kunnen toepassingen bestanden efficiënt uitbreiden zonder dat er een prestatieboete wordt opgelegd voor het vullen ervan.

Omdat het NTFS-bestandssysteem de gegevens niet op nul vult tot de geldige gegevenslengte (VDL) die is gedefinieerd door SetFileValidData, heeft deze functie gevolgen voor de beveiliging waarbij het bestand clusters kan worden toegewezen die eerder door andere bestanden werden bezet. SetFileValidData Daarom vereist dat de aanroeper de nieuwe SeManageVolumePrivilege heeft ingeschakeld (dit wordt standaard alleen toegewezen aan beheerders). Microsoft raadt onafhankelijke softwareleveranciers (ISV's) aan om zorgvuldig na te denken over de gevolgen van het gebruik van een dergelijke functie.

Cache

De meeste I/O-stuurprogramma's (schijf, communicatie en andere) hebben een speciale casecode waarbij, als een I/O-aanvraag onmiddellijk kan worden voltooid, de bewerking wordt voltooid en de ReadFile functie of WriteFileWAAR retourneert. In alle opzichten lijken deze typen bewerkingen synchroon te zijn. Voor een schijfapparaat kan een I/O-aanvraag doorgaans onmiddellijk worden voltooid wanneer de gegevens in het geheugen in de cache worden opgeslagen.

Gegevens bevinden zich niet in de cache

Het cacheschema kan echter tegen u werken als de gegevens zich niet in de cache bevinden. De Windows-cache wordt intern geïmplementeerd met behulp van bestandstoewijzingen. Geheugenbeheer in Windows biedt geen asynchrone paginafoutmechanisme voor het beheren van de bestandstoewijzingen die door cachebeheer worden gebruikt. Het cachebeheer kan controleren of de aangevraagde pagina zich in het geheugen bevindt, dus als u een asynchrone leesbewerking in de cache uitgeeft en de pagina's zich niet in het geheugen bevinden, gaat het stuurprogramma van het bestandssysteem ervan uit dat u de thread niet wilt blokkeren en wordt de aanvraag verwerkt door een beperkte groep werkrolthreads. Het besturingselement wordt na uw aanroep teruggeleid naar uw ReadFile programma, waarbij de leesbewerking nog in behandeling is.

Dit werkt prima voor een klein aantal aanvragen, maar omdat de groep met werk threads beperkt is (momenteel drie op een systeem van 16 MB), zijn er nog steeds slechts enkele aanvragen in de wachtrij bij het schijfstuurprogramma op een bepaald moment. Als u talloze I/O-bewerkingen uitvoert voor gegevens die zich niet in de cache bevinden, raken cachebeheer en geheugenbeheer verzadigd en worden uw aanvragen synchroon uitgevoerd.

Het gedrag van het cachebeheer kan ook worden beïnvloed op basis van het feit of u een bestand sequentieel of willekeurig opent. De voordelen van de cache worden het meest gezien bij het achter elkaar openen van bestanden. Met FILE_FLAG_SEQUENTIAL_SCAN de vlag in de CreateFile aanroep wordt de cache geoptimaliseerd voor dit type toegang. Als u bestanden echter op een willekeurige manier opent, gebruikt u de FILE_FLAG_RANDOM_ACCESS vlag in CreateFile om de cachebeheerder te instrueren om het gedrag te optimaliseren voor willekeurige toegang.

De cache niet gebruiken

De FILE_FLAG_NO_BUFFERING vlag heeft het meeste effect op het gedrag van het bestandssysteem voor asynchrone bewerkingen. Dit is de beste manier om te garanderen dat I/O-aanvragen asynchroon zijn. Het geeft het bestandssysteem de opdracht om helemaal geen cachemechanisme te gebruiken.

Opmerking

Er gelden enkele beperkingen voor het gebruik van deze vlag die te maken hebben met de uitlijning van de gegevensbuffer en de sectorgrootte van het apparaat. Zie de functieverwijzing in de documentatie voor de functie CreateFile over het correct gebruiken van deze vlag voor meer informatie.

Testresultaten in de praktijk

Hieronder volgen enkele testresultaten van de voorbeeldcode. De grootte van de getallen is hier niet belangrijk en varieert van computer tot computer, maar de relatie van de getallen ten opzichte van elkaar verlicht het algemene effect van de vlaggen op de prestaties.

U kunt resultaten verwachten die vergelijkbaar zijn met een van de volgende:

  • Test 1

    Asynchronous, unbuffered I/O:  asynchio /f*.dat /n
    Operations completed out of the order in which they were requested.
       500 requests queued in 0.224264 second.
       500 requests completed in 4.982481 seconds.
    

    Deze test toont aan dat het eerder genoemde programma snel 500 I/O-aanvragen heeft uitgegeven en veel tijd had om ander werk uit te voeren of meer aanvragen uit te voeren.

  • Test 2

    Synchronous, unbuffered I/O: asynchio /f*.dat /s /n
        Operations completed in the order issued.
        500 requests queued and completed in 4.495806 seconds.
    

    Deze test toont aan dat dit programma 4,495880 seconden heeft besteed aan het aanroepen van ReadFile om de bewerkingen te voltooien, maar test 1 heeft slechts 0,224264 seconde besteed aan het uitgeven van dezelfde aanvragen. In test 2 was er geen extra tijd voor het programma om achtergrondwerk uit te voeren.

  • Test 3

    Asynchronous, buffered I/O: asynchio /f*.dat
        Operations completed in the order issued.
        500 requests issued and completed in 0.251670 second.
    

    Deze test demonstreert de synchrone aard van de cache. Alle leesbewerkingen zijn uitgegeven en voltooid in 0,251670 seconde. Met andere woorden, asynchrone aanvragen zijn synchroon voltooid. Deze test toont ook de hoge prestaties van het cachebeheer wanneer gegevens zich in de cache bevinden.

  • Test 4

    Synchronous, buffered I/O: asynchio /f*.dat /s
        Operations completed in the order issued.
        500 requests and completed in 0.217011 seconds.
    

    Deze test toont dezelfde resultaten als in test 3. Synchrone leesbewerkingen uit de cache worden iets sneller voltooid dan asynchrone leesbewerkingen uit de cache. Deze test toont ook de hoge prestaties van het cachebeheer wanneer gegevens zich in de cache bevinden.

Conclusie

U kunt bepalen welke methode het beste is, omdat dit allemaal afhankelijk is van het type, de grootte en het aantal bewerkingen dat uw programma uitvoert.

De standaard bestandstoegang zonder speciale vlaggen op te geven, CreateFile is een synchrone bewerking in de cache.

Opmerking

U krijgt in deze modus wel wat automatisch asynchroon gedrag omdat het bestandssysteemstuurprogramma voorspellende asynchrone lees- en asynchrone luie schrijfbewerkingen uitvoert van gewijzigde gegevens. Hoewel dit gedrag de I/O van de toepassing niet asynchroon maakt, is dit het ideale geval voor de overgrote meerderheid van eenvoudige toepassingen.

Als uw toepassing daarentegen niet eenvoudig is, moet u mogelijk profilering en prestatiebewaking uitvoeren om de beste methode te bepalen, vergelijkbaar met de tests die eerder in dit artikel zijn beschreven. Het is handig om de tijd die in de ReadFile functie of WriteFile is besteed te profileren en deze tijd vervolgens te vergelijken met hoe lang het duurt voordat de werkelijke I/O-bewerkingen zijn voltooid. Als het merendeel van de tijd wordt besteed aan het daadwerkelijk uitgeven van de I/O, wordt uw I/O synchroon voltooid. Als de tijd die wordt besteed aan het uitgeven van I/O-aanvragen echter relatief klein is in vergelijking met de tijd die nodig is om de I/O-bewerkingen te voltooien, worden uw bewerkingen asynchroon behandeld. De voorbeeldcode die eerder in dit artikel wordt genoemd, maakt gebruik van de QueryPerformanceCounter functie om een eigen interne profilering uit te voeren.

Prestatiebewaking kan helpen bepalen hoe efficiënt uw programma de schijf en de cache gebruikt. Als u de prestatiemeteritems voor het Cache-object bijhoudt, wordt de prestaties van cachebeheer aangegeven. Het bijhouden van de prestatiemeteritems voor de objecten Fysieke schijf of Logische schijf geeft de prestaties van de schijfsystemen aan.

Er zijn verschillende hulpprogramma's die nuttig zijn bij het bewaken van de prestaties. PerfMon en DiskPerf vooral nuttig zijn. Als u wilt dat het systeem gegevens over de prestaties van de schijfsystemen verzamelt, moet u eerst de DiskPerf opdracht geven. Nadat u de opdracht hebt uitgevoerd, moet u het systeem opnieuw opstarten om de gegevensverzameling te starten.

Verwijzingen

Synchrone en asynchrone I/O