Sidcache cachelagrar nyligen använda sidor från hårddisken, vilket minskar söktiderna för efterföljande åtkomst till samma data. Sidcachen förbättrar inte prestandan första gången en sida öppnas från hårddisken. Så om en app ska läsa en fil en gång och bara en gång, är det bättre att kringgå sidcachen. Detta är möjligt genom att använda flaggan O_DIRECT. Detta innebär att kärnan inte tar hänsyn till just denna data för sidcachen. Att minska cache-konkurrens innebär att andra sidor (som skulle nås upprepade gånger) har en bättre chans att behållas i sidcachen. Detta förbättrar cache-träff-förhållandet, vilket ger bättre prestanda.
void ioReadOnceFile()
{
/* Att använda direct_fd och direct_f kringgår kernelsidans cache.
* - direct_fd är en filbeskrivning
på låg nivå* - direct_f är en filström som liknar en som returneras av fopen()
* OBS: Använd getpagesize() för att fastställa buffertar med optimal storlek.
*/
int direct_fd = open("filnamn", O_DIRECT | O_RDWR).
FIL *direct_f = fdopen(direct_fd, "w+");
/* direct disk-I/O gjort HÄR*/
fclose(f);
close(fd);
}
Tänk på fallet med en läsning i en stor fil (dvs. en databas) som består av ett stort antal sidor. Varje efterföljande sida som öppnas hamnar i sidcachen bara för att senare tas bort när fler och fler sidor läses. Detta minskar avsevärt cache-träff-förhållandet. I det här fallet ger sidcachen inga prestandavinster. Därför skulle det vara bättre att kringgå sidcachen när man kommer åt stora filer.
void ioLargeFile()
{
/* Att använda direct_fd och direct_f förbigår kärnans sidcache.
* - direct_fd är en filbeskrivning
på låg nivå* - direct_f är en filström som liknar en som returneras av fopen()
* OBS: Använd getpagesize() för att fastställa buffertar med optimal storlek.
*/
int direct_fd = open("largefile.bin", O_DIRECT | O_RDWR | O_LARGEFILE).
FIL *direct_f = fdopen(direct_fd, "w+");
/* direct disk-I/O gjort HÄR*/
fclose(f);
close(fd);
}
IO-schemaläggaren optimerar ordningen på I/O-åtgärder som ska placeras i kö på hårddisken. Eftersom söktid är den tyngsta belastningen på en hårddisk försöker de flesta I/O-schemaläggare att minimera söktiden. Detta implementeras som en variant av hissalgoritmen, dvs. att ordna om de slumpmässigt beställda förfrågningarna från många processer till den ordning i vilken data finns på hårddisken, vilket kräver en betydande mängd CPU-tid.
Vissa uppgifter som omfattar komplexa åtgärder tenderar att begränsas av hur snabbt processorn kan bearbeta stora mängder data. En komplex I/O-schemaläggare som körs i bakgrunden kan förbruka dyrbara CPU-cykler och därmed minska systemets prestanda. I det här fallet minskar byte till en enklare algoritm som no-op CPU-belastningen och kan förbättra systemets prestanda.
echo noop > /sys/block//queue/scheduler
Även om detta så småningom kommer att få jobbet gjort, är det definitivt inte det mest optimala sättet. Ur kernelns perspektiv är den mest optimala storleken för I/O-begäranden filsystemets blockstorlek (d.v.s. sidstorleken). Eftersom all I/O i filsystemet (och kärnans sidcache) är i termer av sidor, är det vettigt för appen att göra överföringar i multiplar av sidstorlek också. Även med multisegmenterade cacheminnen som tar sig in i hårddiskar nu, skulle man ha stor nytta av att göra I/O i multiplar av blockstorlek.
Följande kommando kan användas för att bestämma den optimala blockstorleken
stat --printf="bs=%s optimal-bs=%S\n" --file-system /dev/
När en app initierar en SYNC I/O-läsning köar kerneln en läsåtgärd för data och returnerar först när hela blocket med begärda data har lästs tillbaka. Under den här perioden markerar kärnan programmets process som blockerad för I/O. Andra processer kan utnyttja CPU:n, vilket resulterar i en övergripande bättre prestanda för systemet.
När en app initierar en SYNC I/O-skrivning köar kerneln en skrivåtgärd för data placerar programmets process i en blockerad I/O. Tyvärr innebär detta att det aktuella programmets process är blockerad och inte kan utföra någon annan bearbetning (eller I/O för den delen) förrän den här skrivåtgärden har slutförts.
När en app initierar en ASYNC I/O-läsning read() returneras funktionen vanligtvis efter läsning av en delmängd av det stora datablocket. Appen måste upprepade gånger anropa read() med storleken på de data som återstår att läsa, tills alla nödvändiga data har lästs in. Varje ytterligare anrop till läsning medför vissa omkostnader eftersom det introducerar en kontextväxling mellan användarutrymmet och kerneln. Implementering av en snäv loop för att upprepade gånger anropa read() slösar bort CPU-cykler som andra processer kunde ha använt. Därför implementerar man vanligtvis blockering med select() tills nästa read() returnerar byte som inte är noll byte. d.v.s. ASYNC är gjord för att blockera precis som SYNC-läsningen gör.
När en app initierar en ASYNC I/O-skrivning uppdaterar kerneln motsvarande sidor i sidcachen och markerar dem som smutsiga. Sedan återgår kontrollen snabbt till appen som kan fortsätta att köras. Data töms till hårddisken senare vid en mer optimal tidpunkt (låg CPU-belastning) på ett mer optimalt sätt (sekventiellt buntade skrivningar).
Därför är SYNC-läsningar och ASYNC-skrivningar vanligtvis en bra väg att gå eftersom de gör det möjligt för kerneln att optimera ordningen och tidpunkten för de underliggande I/O-begärandena.