Qu’est-ce qui met à jour mtime après avoir écrit dans des fichiers mappés en mémoire?

J’utilise XFS sous Linux et j’ai un fichier mappé en mémoire sur lequel j’écris une fois par seconde. Je remarque que le fichier mtime (montré par watch ls --full-time ) change périodiquement mais irrégulièrement. L’écart entre les temps semble se situer entre 2 et 20 secondes, mais il n’est pas cohérent. Il n’ya que très peu d’autres éléments fonctionnant sur le système – en particulier il n’ya qu’un seul programme qui écrit le fichier, plus une lecture.

Le même programme écrit beaucoup plus fréquemment sur certains autres fichiers mappés et leur mtime change exactement une fois toutes les 30 secondes.

Je n’utilise pas msync() (qui mettrait à jour mtime lorsqu’il est appelé).

Mes questions:

  1. Quelles mises à jour mtime?
  2. L’intervalle de mise à jour est-il configurable?
  3. Pourquoi certains mtimes sont-ils mis à jour exactement une fois toutes les 30 secondes, mais certains fichiers que j’écris moins fréquemment ont des temps plus frais (irréguliers mais toujours inférieurs à 30 secondes)?

Lorsque vous mmap un fichier, vous partagez essentiellement la mémoire directement entre votre processus et le cache de la page du kernel – le même cache qui contient les données de fichier qui ont été lues depuis le disque ou qui attendent d’être écrites sur le disque. Une page du cache de la page qui est différente de celle du disque (parce qu’elle a été écrite) est appelée “sale”.

Il existe un thread du kernel qui recherche les pages sales et les réécrit sur le disque, sous le contrôle de plusieurs parameters. Un important est dirty_expire_centisecs . Si l’une des pages d’un fichier est sale depuis plus longtemps que dirty_expire_centisecs toutes les pages dirty_expire_centisecs de ce fichier seront écrites. La valeur par défaut est 3000 centisecs (30 secondes).

Un autre ensemble de variables est dirty_writeback_centisecs , dirty_background_ratio et dirty_ratio . dirty_writeback_centisecs contrôle la fréquence à laquelle le thread du kernel vérifie la dirty_writeback_centisecs et leur dirty_writeback_centisecs défaut de 500 (5 secondes). Si le pourcentage de pages dirty_background_ratio (sous forme de fraction de la mémoire disponible pour la mise en cache) est inférieur à dirty_background_ratio rien ne se produit; si c’est plus que dirty_background_ratio , le kernel commencera à écrire des pages sur le disque. Enfin, si le pourcentage de pages dirty_ratio dépasse dirty_ratio , tous les processus tentant d’écrire seront dirty_ratio jusqu’à ce que la quantité de données dirty_ratio diminue. Cela garantit que la quantité de données non écrites ne peut pas augmenter sans limite; À terme, les processus produisant des données plus rapidement que ce que le disque peut écrire devront ralentir pour correspondre au rythme du disque.

La question de savoir comment le mtime est mis à jour est liée à la question de savoir comment le kernel sait qu’une page est sale en premier lieu. Dans le cas de mmap , la réponse est que le kernel définit les pages du mappage en lecture seule. Cela ne signifie pas que vous ne pouvez pas les écrire, mais cela signifie que la première fois, cela déclenche une exception dans l’unité de gestion de la mémoire, qui est gérée par le kernel. Le gestionnaire d’exceptions fait au moins quatre choses:

  1. Marque la page comme étant sale, pour qu’elle soit réécrite.
  2. Met à jour le fichier mtime.
  3. Marque la page en lecture-écriture, pour que l’écriture puisse réussir.
  4. Revient à l’instruction de votre programme qui écrit sur la page mmap ed, qui réussit cette fois.

Ainsi, lorsque vous écrivez des données sur une page propre, cela provoque une mise à jour de mtime, mais cela entraîne également la lecture-écriture de la page, de sorte que d’autres écritures ne provoquent pas d’exception (ou de mise à jour de mtime). Cependant, lorsque la page en question est vidée sur le disque, elle devient propre et redevient “en lecture seule”, de sorte que toute nouvelle écriture provoquera une autre écriture sur disque, ainsi qu’une autre mise à jour de mtime.

Alors maintenant, avec quelques hypothèses, nous pouvons commencer à rassembler le puzzle.

En premier lieu, dirty_background_ratio et dirty_ratio sont probablement pas en jeu. Si le rythme de vos écritures était assez rapide pour déclencher des vidages en arrière-plan, vous verriez probablement le comportement “irrégulier” sur tous les fichiers.

Deuxièmement, la différence entre les fichiers “irréguliers” et les fichiers “30 secondes” est le modèle d’access aux pages. Je suppose que les fichiers “irréguliers” sont écrits dans une sorte de mode append ou de tampon circulaire, de sorte que vous commencez à écrire sur une nouvelle page toutes les quelques secondes. Chaque fois que vous enregistrez une page précédemment intacte, elle déclenche une mise à jour de mtime. Mais pour les fichiers affichant le modèle de 30 secondes, vous écrivez uniquement sur une page (peut-être que leur longueur ne dépasse pas une page). Dans ce cas, le mtime est mis à jour à la première écriture, puis à nouveau tant que le fichier n’a pas été dirty_expire_centisecs sur disque en dépassant dirty_expire_centisecs , ce qui représente 30 secondes.

Note 1: Ce comportement est techniquement incorrect. C’est imprévisible, mais les normes permettent un certain degré d’imprévisibilité. Mais ils requièrent que le mtime soit quelquefois à ou après la dernière écriture dans un fichier, et à ou avant une msync (le cas échéant). Dans le cas où une page est écrite à plusieurs resockets dans l’intervalle avant son vidage sur disque, ce n’est pas ce qui arrive – le mtime obtient l’horodatage de la première écriture. Cela a été discuté, mais un patch qui l’aurait corrigé n’a pas été accepté. Par conséquent, lorsque vous utilisez mmap , mtimes peut être en erreur. dirty_expire_centisecs limite en dirty_expire_centisecs sorte cette erreur, mais seulement partiellement, car un autre trafic de disque peut dirty_expire_centisecs à attendre, étendant la fenêtre pour une écriture afin de contourner encore mtime.