Paginacache slaat recent geopende pagina's van de harde schijf op, waardoor zoektijden voor volgende toegang tot dezelfde gegevens worden verkort. De paginacache verbetert de prestaties niet wanneer een pagina voor het eerst wordt geopend vanaf de harde schijf. Dus als een app een bestand één keer gaat lezen en slechts één keer, dan is het omzeilen van de paginacache de betere manier om te gaan. Dit is mogelijk door gebruik te maken van de O_DIRECT vlag. Dit betekent dat de kernel geen rekening houdt met deze specifieke gegevens voor de paginacache. Het verminderen van cache-conflicten betekent dat andere pagina's (die herhaaldelijk zouden worden geopend) een betere kans hebben om in de paginacache te worden bewaard. Dit verbetert de cache-hit ratio, wat zorgt voor betere prestaties.
void ioReadOnceFile()
{
/* Het gebruik van direct_fd en direct_f omzeilt de kernel page-cache.
* - direct_fd is een bestandsdescriptor
op laag niveau* - direct_f is een bestandsstroom die lijkt op een die wordt geretourneerd door fopen()
* OPMERKING: Gebruik getpagesize() voor het bepalen van buffers van optimale grootte.
*/
int direct_fd = open("bestandsnaam", O_DIRECT | O_RDWR);
BESTAND *direct_f = fdopen(direct_fd, "w+");
/* directe schijf-I/O gedaan HIER*/
fclose(f);
Sluiten(FD);
}
Beschouw het geval van een lezing in een groot bestand (d.w.z. een database) dat uit een groot aantal pagina's bestaat. Elke volgende pagina die wordt geopend, gaat naar de paginacache om later te worden verwijderd als er meer en meer pagina's worden gelezen. Dit vermindert de cache-hit ratio aanzienlijk. In dit geval levert de page-cache geen prestatiewinst op. Daarom is het beter om de paginacache te omzeilen bij het openen van grote bestanden.
void ioLargeFile()
{
/* Met behulp van direct_fd en direct_f omzeilt u de kernel page-cache.
* - direct_fd is een bestandsdescriptor
op laag niveau* - direct_f is een bestandsstroom die lijkt op een die wordt geretourneerd door fopen()
* OPMERKING: Gebruik getpagesize() voor het bepalen van buffers van optimale grootte.
*/
int direct_fd = open("largefile.bin", O_DIRECT | O_RDWR | O_LARGEFILE);
BESTAND *direct_f = fdopen(direct_fd, "w+");
/* directe schijf-I/O gedaan HIER*/
fclose(f);
Sluiten(FD);
}
De io-scheduler optimaliseert de volgorde van I/O-bewerkingen die op de harde schijf in de wachtrij moeten worden geplaatst. Aangezien de zoektijd de zwaarste straf is op een harde schijf, proberen de meeste I/O-planners de zoektijd te minimaliseren. Dit wordt geïmplementeerd als een variant van het liftalgoritme, d.w.z. het herschikken van de willekeurig geordende verzoeken van talrijke processen naar de volgorde waarin de gegevens op de harde schijf aanwezig zijn, vereist een aanzienlijke hoeveelheid CPU-tijd.
Bepaalde taken die complexe bewerkingen omvatten, worden meestal beperkt door de snelheid waarmee de CPU grote hoeveelheden data kan verwerken. Een complexe I/O-planner die op de achtergrond wordt uitgevoerd, kan kostbare CPU-cycli verbruiken, waardoor de systeemprestaties afnemen. In dit geval vermindert de overstap naar een eenvoudiger algoritme, zoals no-op, de CPU-belasting en kan het de systeemprestaties verbeteren.
echo noop > /sys/block//queue/scheduler
Hoewel dit uiteindelijk de klus zal klaren, is het zeker niet de meest optimale manier. Vanuit het perspectief van de kernel is de meest optimale grootte voor I/O-verzoeken de blokgrootte van het bestandssysteem (d.w.z. de paginagrootte). Aangezien alle I/O in het bestandssysteem (en de kernel page-cache) in termen van pagina's is, is het logisch dat de app ook overdrachten doet in veelvouden van paginagrootte. Ook met caches met meerdere segmenten die nu hun weg vinden naar harde schijven, zou men enorm profiteren van het doen van I/O in veelvouden van blokgrootte.
De volgende opdracht kan worden gebruikt om de optimale block-size
stat te bepalen--printf="bs=%s optimal-bs=%S\n" --file-system /dev/
Wanneer een app een SYNC I/O-lezing initieert, zet de kernel een leesbewerking voor de data in de wachtrij en keert pas terug nadat het hele blok met gevraagde data is teruggelezen. Gedurende deze periode markeert de kernel het proces van de applicatie als geblokkeerd voor I/O. Andere processen kunnen gebruikmaken van de CPU, wat resulteert in algehele betere prestaties voor het systeem.
Wanneer een app een SYNC I/O-schrijfbewerking initieert, plaatst de kernel een schrijfbewerking in de wachtrij voor de gegevens, waardoor het proces van de applicatie in een geblokkeerde I/O wordt geplaatst. Helaas betekent dit dat het proces van de huidige applicatie is geblokkeerd en geen andere verwerking (of I/O wat dat betreft) kan worden uitgevoerd totdat deze schrijfbewerking is voltooid.
Wanneer een app een asynchrone I/O-leesbewerking start, keert de functie read() meestal terug na het lezen van een subset van het grote datablok. De app moet herhaaldelijk read() aanroepen met de grootte van de data die nog moet worden gelezen, totdat de volledige vereiste data is ingelezen. Elke extra aanroep om te lezen introduceert enige overhead omdat het een context-switch tussen de gebruikersruimte en de kernel introduceert. Door een strakke lus te implementeren om herhaaldelijk read() aan te roepen, worden CPU-cycli verspild die andere processen hadden kunnen gebruiken. Vandaar dat men meestal blokkering implementeert met behulp van select() totdat de volgende read() niet-nul bytes read-in retourneert. d.w.z. de ASYNC is gemaakt om te blokkeren, net zoals de SYNC read dat doet.
Wanneer een app een asynchrone I/O-schrijfactie start, werkt de kernel de corresponderende pagina's in de paginacache bij en markeert ze als vuil. Dan keert de besturing snel terug naar de app die kan blijven draaien. De gegevens worden later op een optimaler tijdstip (lage CPU-belasting) op een meer optimale manier (sequentieel gebundelde schrijfbewerkingen) naar de harde schijf gespoeld.
Daarom zijn SYNC-reads en ASYNC-writes over het algemeen een goede manier om te gaan, omdat ze de kernel in staat stellen de volgorde en timing van de onderliggende I/O-verzoeken te optimaliseren.