Le fichier append est-il atomique sous UNIX?

106

En général, que pouvons-nous prendre pour acquis lorsque nous ajoutons à un fichier sous UNIX à partir de plusieurs processus? Est-il possible de perdre des données (un processus écrasant les modifications de l'autre)? Est-il possible que les données soient mutilées? (Par exemple, chaque processus ajoute une ligne par ajout à un fichier journal, est-il possible que deux lignes soient mutilées?) Si l'ajout n'est pas atomique dans le sens ci-dessus, quel est le meilleur moyen d'assurer l'exclusion mutuelle?

Lajos Nagy
la source

Réponses:

65

Une écriture dont la taille est inférieure à 'PIPE_BUF' est supposée être atomique. Cela devrait être d'au moins 512 octets, bien que cela puisse facilement être plus grand (Linux semble l'avoir réglé à 4096).

Cela suppose que vous parlez de tous les composants entièrement compatibles POSIX. Par exemple, ce n'est pas vrai sur NFS.

Mais en supposant que vous écriviez dans un fichier journal que vous avez ouvert en mode 'O_APPEND' et que vous gardiez vos lignes (y compris le saut de ligne) sous 'PIPE_BUF' octets de long, vous devriez pouvoir avoir plusieurs écrivains dans un fichier journal sans aucun problème de corruption. Toutes les interruptions arriveront avant ou après l'écriture, pas au milieu. Si vous voulez que l'intégrité du fichier survive à un redémarrage, vous devrez également appeler fsync(2)après chaque écriture, mais c'est terrible pour les performances.

Clarification : lisez les commentaires et la réponse d'Oz Solomon . Je ne suis pas sûr que ce O_APPENDsoit censé avoir cette PIPE_BUFatomicité de taille. Il est tout à fait possible que ce soit simplement la manière dont Linux a été implémenté write(), ou cela peut être dû à la taille des blocs du système de fichiers sous-jacent.

freiheit
la source
11
Sur des systèmes de fichiers sains, fsync(2)donne autant de garantie que le sync(2)fait et n'a pas autant d'impact sur les performances.
éphémère
4
Êtes-vous sûr de cela? Pourriez-vous fournir un lien sur ce comportement? Je l'ai trouvé confirmé si le descripteur est un tube, mais je n'ai pas trouvé de preuve que cela fonctionne pour n'importe quel fichier. y compris les objets de fichier normaux non NFS.
Alan Franzoni
6
Où exactement dans ... / write.html? Pour O_APPEND, je ne vois aucune mention de PIPE_BUF, et je vois la promesse qu ' "aucune opération de modification de fichier ne se produira entre la modification du décalage du fichier et l'opération d'écriture" , mais je ne suis pas sûr que cela signifie que l'opération d'écriture elle-même est ininterrompu ...
akavel
6
Comme le souligne cette réponse , la déclaration PIPE_BUFsur cette page ne s'applique qu'aux tubes et FIFO, pas aux fichiers normaux.
Greg Inozemtsev
3
Avec l'arrivée de signaux, cela peut être encore pire: bugzilla.kernel.org/show_bug.cgi?id=55651 . Pourquoi est-ce même marqué comme une réponse? PIPE_BUF n'a rien à voir avec les fichiers.
thinred
35

Edit: mis à jour en août 2017 avec les derniers résultats Windows.

Je vais vous donner une réponse avec des liens vers le code de test et les résultats en tant qu'auteur du projet Boost.AFIO qui implémente un système de fichiers asynchrone et une bibliothèque C ++ d' entrées / sorties de fichiers.

Premièrement, O_APPEND ou l'équivalent FILE_APPEND_DATA sous Windows signifie que les incréments de l'étendue maximale du fichier ("longueur" du fichier) sont atomiques sous les auteurs simultanés. Ceci est garanti par POSIX et Linux, FreeBSD, OS X et Windows l'implémentent tous correctement. Samba l'implémente également correctement, NFS avant la v5 ne le faisant pas car il ne dispose pas de la capacité de formatage filaire pour l'ajout atomique. Ainsi, si vous ouvrez votre fichier avec un ajout uniquement, les écritures simultanées ne se déchireront pas les unes par rapport aux autres sur un système d'exploitation majeur, sauf si NFS est impliqué.

Cependant, les lectures simultanées aux appendices atomiques peuvent voir des écritures déchirées en fonction du système d'exploitation, du système de fichiers et des indicateurs avec lesquels vous avez ouvert le fichier - l'incrément de l'étendue maximale du fichier est atomique, mais la visibilité des écritures par rapport aux lectures peut ou non être atomique. Voici un résumé rapide par drapeaux, OS et système de classement:


Non O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 avec NTFS: mise à jour atomicité = 1 octet jusqu'à 10.0.10240 inclus, à partir de 10.0.14393 au moins 1 Mo, probablement infini (*).

Linux 4.2.6 avec ext4: mise à jour de l'atomicité = 1 octet

FreeBSD 10.2 avec ZFS: mise à jour de l'atomicité = au moins 1 Mo, probablement infini (*)

O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 avec NTFS: mise à jour atomicité = jusqu'à et y compris 10.0.10240 jusqu'à 4096 octets uniquement si la page est alignée, sinon 512 octets si FILE_FLAG_WRITE_THROUGH est désactivé, sinon 64 octets. Notez que cette atomicité est probablement une fonctionnalité de PCIe DMA plutôt que conçue. Depuis 10.0.14393, au moins 1 Mo, probablement infini (*).

Linux 4.2.6 avec ext4: mise à jour atomicité = au moins 1 Mo, probablement infini (*). Notez que les Linux précédents avec ext4 ne dépassaient certainement pas 4096 octets, XFS avait certainement un verrouillage personnalisé, mais il semble que Linux récent a finalement résolu ce problème.

FreeBSD 10.2 avec ZFS: mise à jour de l'atomicité = au moins 1 Mo, probablement infini (*)


Vous pouvez voir les résultats des tests empiriques bruts sur https://github.com/ned14/afio/tree/master/programs/fs-probe . Notez que nous testons les décalages déchirés uniquement sur des multiples de 512 octets, donc je ne peux pas dire si une mise à jour partielle d'un secteur de 512 octets se déchirerait pendant le cycle de lecture-modification-écriture.

Donc, pour répondre à la question du PO, les écritures O_APPEND n'interféreront pas les unes avec les autres, mais les lectures simultanées aux écritures O_APPEND verront probablement des écritures déchirées sur Linux avec ext4 à moins que O_DIRECT ne soit activé, après quoi vos écritures O_APPEND devront être d'un multiple de taille de secteur.


(*) "Probablement infini" provient de ces clauses de la spécification POSIX:

Toutes les fonctions suivantes doivent être atomiques les unes par rapport aux autres dans les effets spécifiés dans POSIX.1-2008 lorsqu'elles opèrent sur des fichiers normaux ou des liens symboliques ... [plusieurs fonctions] ... read () ... write ( ) ... Si deux threads appellent chacun l'une de ces fonctions, chaque appel verra soit tous les effets spécifiés de l'autre appel, soit aucun d'entre eux. [La source]

et

Les écritures peuvent être sérialisées par rapport à d'autres lectures et écritures. Si une lecture () de données de fichier peut être prouvée (par n'importe quel moyen) après une écriture () des données, elle doit refléter cette écriture (), même si les appels sont effectués par des processus différents. [La source]

mais inversement:

Ce volume de POSIX.1-2008 ne spécifie pas le comportement des écritures simultanées dans un fichier à partir de plusieurs processus. Les applications doivent utiliser une forme de contrôle d'accès concurrentiel. [La source]

Vous pouvez en savoir plus sur la signification de ceux-ci dans cette réponse

Niall Douglas
la source
29

J'ai écrit un script pour tester empiriquement la taille maximale de l'append atomique. Le script, écrit en bash, génère plusieurs processus de travail qui écrivent tous des signatures spécifiques au travailleur dans le même fichier. Il lit ensuite le fichier à la recherche de signatures qui se chevauchent ou sont corrompues. Vous pouvez voir la source du script dans cet article de blog .

La taille maximale réelle de l'ajout atomique varie non seulement selon le système d'exploitation, mais aussi selon le système de fichiers.

Sous Linux + ext3, la taille est de 4096 et sous Windows + NTFS, la taille est de 1024. Voir les commentaires ci-dessous pour plus de tailles.

Oz Solomon
la source
Avec quel système de fichiers avez-vous testé sous Linux? Je me demande si cela est peut-être basé sur la taille des blocs du système de fichiers.
freiheit
@freiheit je crois qu'à l'époque je l'ai testé sur ext3. Si vous l'exécutez sur un autre FS et obtenez un résultat différent, veuillez poster un commentaire.
Oz Solomon
3
@OzSolomon, j'ai utilisé votre script sur Debian 7.8, et je n'ai pu obtenir que des écritures atomiques jusqu'à 1008 octets inclus (1024 - 16 octets de surcharge?) À la fois sur ma partition ext4 et un montage tmpfs. Tout ce qui était au-delà de cela aboutissait à la corruption à chaque fois.
Eric Pruitt
6
Votre test semble supposer qu'il echo $line >> $OUTPUT_FILEen résultera un seul appel, writequelle que soit la taille de $line.
Tomas
16

Voici ce que dit la norme: http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html .

Si l' O_APPENDindicateur des indicateurs d'état de fichier est mis, le décalage de fichier doit être mis à la fin du fichier avant chaque écriture et aucune opération de modification de fichier ne doit intervenir entre le changement de l'offset de fichier et l'opération d'écriture.

Bastien Léonard
la source
20
"entre" - mais qu'en est-il des interventions lors de l'écriture, qui pour ma compréhension se produisent après le "entre"? (Ie: <change_offset_action> ... "the_between_period" ... <write_action>) - dois-je comprendre qu'il n'y a aucune garantie à ce sujet?
akavel
@akavel était d'accord; il n'y a aucune garantie que l'écriture elle-même soit atomique. Mais je suis confus: sur la base de la garantie fournie dans votre devis, il semble que nous pouvons conclure qu'une application multithread ajoutant le même fichier ne mélangera pas des parties de différents enregistrements écrits. Cependant, d'après les expériences rapportées par OzSolomon, nous voyons que même cette hypothèse est violée. Pourquoi?
max
@max désolé, j'ai bien peur de ne pas comprendre votre question: premièrement, l'expérience d'OzSolomon est multi- processus , pas une application multi- thread (processus unique); deuxièmement, je ne comprends pas comment vous tirez la conclusion qu ' "une application multithread [...] ne se mélangera pas" - c'est exactement ce que je ne vois pas garanti par la citation de Bastien, comme je le mentionne dans mon commentaire. Pouvez-vous clarifier votre question?
akavel
2
Hmm, je ne peux pas reconstruire ma propre logique au moment où j'ai écrit ce commentaire ... Oui, si votre interprétation est correcte, alors bien sûr, les différents enregistrements peuvent être mélangés. Mais maintenant que je relis la citation de Bastien, je pense que cela doit signifier que personne ne peut interrompre "pendant l'écriture" - sinon le paragraphe entier de la norme serait inutile, ne fournissant littéralement aucune garantie (même pas que l'écriture se produira à la fin, car quelqu'un d'autre pourrait déplacer le décalage pendant que l'étape "d'écriture" est en cours d'exécution
max