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 WriteFile
uit 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 WriteFile
WAAR 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.