Le cache de page met en cache les pages récemment consultées à partir du disque dur, réduisant ainsi les temps de recherche pour les accès ultérieurs aux mêmes données. Le cache de page n’améliore pas les performances lors de la première consultation d’une page à partir du disque dur. Donc, si une application doit lire un fichier une fois et une seule fois, le contournement du cache de page est la meilleure solution. Cela est possible à l’aide de la balise O_DIRECT. Cela signifie que le noyau ne prend pas en compte ces données particulières pour le cache de page. La réduction des conflits d’accès au cache signifie que d’autres pages (qui seraient consultées à plusieurs reprises) ont de meilleures chances d’être conservées dans le cache de page. Cela améliore le taux d’accès dans le cache, offrant de meilleures performances.
void ioReadOnceFile()
{
/* L’utilisation de direct_fd et direct_f contourne le cache de page du noyau.
* - direct_fd est un descripteur
de fichier de bas niveau* - direct_f s’agit d’un filestream similaire à celui renvoyé par fopen()
* REMARQUE : Utilisez getpagesize() pour déterminer les mémoires tampons de taille optimale.
*/
int direct_fd = open(« filename », O_DIRECT | O_RDWR) ;
FICHIER *direct_f = fdopen(direct_fd, « w+ ») ;
/* E/S directes de disque réalisées ICI*/
fclose(f) ;
close(fd) ;
}
Prenons le cas d’une lecture dans un fichier volumineux (c’est-à-dire une base de données) composé d’un grand nombre de pages. Chaque page consultée par la suite va dans le cache de page pour être supprimée plus tard au fur et à mesure que de plus en plus de pages sont lues. Cela réduit considérablement le taux d’accès dans le cache. Dans ce cas, le cache de page n’offre aucun gain de performances. Par conséquent, il serait préférable de contourner le cache de page lors de l’accès à des fichiers volumineux.
void ioLargeFile()
{
/* L’utilisation de direct_fd et direct_f contourne le cache de page du noyau.
* - direct_fd est un descripteur
de fichier de bas niveau* - direct_f s’agit d’un filestream similaire à celui renvoyé par fopen()
* REMARQUE : Utilisez getpagesize() pour déterminer les mémoires tampons de taille optimale.
*/
int direct_fd = open(« largefile.bin », O_DIRECT | O_RDWR | O_LARGEFILE) ;
FICHIER *direct_f = fdopen(direct_fd, « w+ ») ;
/* E/S directes de disque réalisées ICI*/
fclose(f) ;
close(fd) ;
}
Le planificateur io optimise l’ordre des opérations d’E/S à mettre en file d’attente sur le disque dur. Étant donné que le temps de recherche est la pénalité la plus lourde sur un disque dur, la plupart des planificateurs d’E/S tentent de minimiser le temps de recherche. Ceci est implémenté en tant que variante de l’algorithme d’ascenseur, c’est-à-dire que la réorganisation des requêtes ordonnées de manière aléatoire à partir de nombreux processus dans l’ordre dans lequel les données sont présentes sur le disque dur nécessite une quantité significative de temps processeur.
Certaines tâches impliquant des opérations complexes ont tendance à être limitées par la vitesse à laquelle le processeur peut traiter de grandes quantités de données. Un planificateur d’E/S complexe s’exécutant en arrière-plan peut consommer de précieux cycles du processeur, réduisant ainsi les performances du système. Dans ce cas, le passage à un algorithme plus simple, tel que l’absence d’opération, réduit la charge du processeur et peut améliorer les performances du système.
echo noop > /sys/block//queue/scheduler
Bien que cela finisse par faire le travail, ce n’est certainement pas le moyen le plus optimal. Du point de vue du noyau, la taille la plus optimale pour les demandes d’E/S est la taille de bloc du système de fichiers (c’est-à-dire la taille de la page). Comme toutes les E/S dans le système de fichiers (et le cache de page du noyau) sont en termes de pages, il est logique que l’application effectue également des transferts en multiples de la taille de la page. De plus, avec l’arrivée de caches multi-segmentés dans les disques durs, il serait extrêmement avantageux d’effectuer des E/S en multiples de la taille des blocs.
La commande suivante peut être utilisée pour déterminer la statistique de taille
de bloc optimale--printf="bs=%s optimal-bs=%S\n » --file-system /dev/
Lorsqu’une application lance une lecture d’E/S SYNC, le noyau met en file d’attente une opération de lecture pour les données et ne renvoie qu’après la lecture de l’ensemble du bloc de données demandées. Pendant cette période, le noyau marque le processus de l’application comme bloqué pour les E/S. D’autres processus peuvent utiliser le processeur, ce qui améliore globalement les performances du système.
Lorsqu’une application lance une écriture d’E/S SYNC, le noyau met en file d’attente une opération d’écriture pour les données qui placent le processus de l’application dans une E/S bloquée. Malheureusement, cela signifie que le processus de l’application en cours est bloqué et ne peut effectuer aucun autre traitement (ou E/S, d’ailleurs) tant que cette opération d’écriture n’est pas terminée.
Lorsqu’une application lance une lecture d’E/S ASYNC, la fonction read() revient généralement après la lecture d’un sous-ensemble du grand bloc de données. L’application doit appeler read() à plusieurs reprises avec la taille des données restant à lire, jusqu’à ce que toutes les données requises soient lues. Chaque appel de lecture supplémentaire introduit une surcharge car il introduit un changement de contexte entre l’espace utilisateur et le noyau. L’implémentation d’une boucle serrée pour les appels répétés read() gaspille des cycles CPU que d’autres processus auraient pu utiliser. Par conséquent, on implémente généralement le blocage à l’aide de select() jusqu’à ce que la prochaine lecture() renvoie une lecture d’octets non nulle. c’est-à-dire que l’ASYNC est conçu pour se bloquer, tout comme la lecture SYNC.
Lorsqu’une application lance une écriture d’E/S ASYNCHRONE, le noyau met à jour les pages correspondantes dans le cache de page et les marque comme non conformes. Ensuite, le contrôle revient rapidement à l’application qui peut continuer à s’exécuter. Les données sont vidées sur le disque dur ultérieurement, à un moment plus optimal (faible charge du processeur) et de manière plus optimale (écritures groupées séquentiellement).
Par conséquent, les lectures et écritures ASYNC sont généralement une bonne solution, car elles permettent au noyau d’optimiser l’ordre et la synchronisation des demandes d’E /S sous-jacentes .