Sous Linux, une exécution terminée d'une commande telle que cp
ou dd
ne signifie pas que les données ont été écrites sur le périphérique. Il faut par exemple appeler sync
, ou appeler la fonction "Safely Remove" ou "Eject" sur le lecteur.
Quelle est la philosophie derrière une telle approche? Pourquoi les données ne sont-elles pas écrites immédiatement? N'y a-t-il aucun danger que l'écriture échoue en raison d'une erreur d'E/S?
Quelle est la philosophie derrière une telle approche?
Efficacité (meilleure utilisation des caractéristiques du disque) et performances (permet à l'application de continuer immédiatement après une écriture).
Pourquoi les données ne sont-elles pas écrites immédiatement?
Le principal avantage est que le système d'exploitation est libre de réorganiser et de fusionner les opérations d'écriture contiguës pour améliorer leur utilisation de la bande passante (moins d'opérations et moins de recherches). Les disques durs fonctionnent mieux lorsqu'un petit nombre de grandes opérations sont demandées, tandis que les applications ont plutôt besoin d'un grand nombre de petites opérations. Une autre optimisation claire est que le système d'exploitation peut également supprimer toutes les écritures sauf la dernière lorsque le même bloc est écrit plusieurs fois dans un court laps de temps, ou même supprimer certaines écritures ensemble si le fichier affecté a été supprimé entre-temps.
Ces écritures asynchrones sont effectuées après l'appel système write
est retourné. Il s'agit du deuxième avantage, le plus visible par les utilisateurs. Les écritures asynchrones accélèrent les applications car elles sont libres de continuer leur travail sans attendre que les données soient réellement sur le disque. Le même type de mise en mémoire tampon/mise en cache est également implémenté pour les opérations de lecture où les blocs récemment ou souvent lus sont conservés en mémoire au lieu d'être relus à partir du disque.
N'y a-t-il aucun danger que l'écriture échoue en raison d'une erreur IO?
Pas nécessairement. Cela dépend du système de fichiers utilisé et de la redondance en place. Une erreur d'E/S peut être inoffensive si les données peuvent être enregistrées ailleurs. Les systèmes de fichiers modernes comme ZFS corrigent automatiquement les blocs de disque défectueux. Notez également que les erreurs d'E/S ne plantent pas les systèmes d'exploitation modernes. S'ils se produisent lors de l'accès aux données, ils sont simplement signalés à l'application concernée. S'ils se produisent lors de l'accès aux métadonnées structurelles et mettent le système de fichiers en danger, il peut être remonté en lecture seule ou rendu inaccessible.
Il existe également un léger risque de perte de données en cas de panne du système d'exploitation, de panne de courant ou de panne matérielle. C'est la raison pour laquelle les applications qui doivent être sûres à 100% des données sur le disque (par exemple, les bases de données/applications financières) effectuent des écritures synchrones moins efficaces mais plus sécurisées. Pour atténuer l'impact sur les performances, de nombreuses applications utilisent toujours des écritures asynchrones mais finissent par les synchroniser lorsque l'utilisateur enregistre explicitement un fichier (par exemple vim, traitements de texte.)
D'un autre côté, une très grande majorité d'utilisateurs et d'applications n'ont pas besoin de la sécurité offerte par les écritures synchrones, ni s'en soucient. En cas de crash ou de panne de courant, le seul risque est souvent de perdre au pire les 30 dernières secondes de données. À moins qu'une transaction financière ne soit impliquée ou quelque chose de similaire qui impliquerait un coût bien supérieur à 30 secondes de leur temps, l'énorme gain de performances (qui n'est pas une illusion mais bien réel) des écritures asynchrones permet largement surpasse le risque.
Enfin, les écritures synchrones ne sont pas suffisantes pour protéger les données écrites de toute façon. Si votre application doit vraiment être sûre que ses données ne peuvent pas être perdues quoi qu'il arrive, la réplication des données sur plusieurs disques et sur plusieurs emplacements géographiques doit être mise en place pour résister aux catastrophes telles que les incendies, les inondations, etc.
Il donne simplement une illusion de vitesse aux programmes qui n'ont pas à attendre la fin d'une écriture. Montez vos systèmes de fichiers en mode synchronisation (ce qui vous donne vos écritures instantanées) et voyez à quel point tout est lent.
Parfois, les fichiers n'existent que temporairement ... un programme fait un peu de travail et supprime le fichier juste après le travail. Si vous avez retardé ces écritures, vous pourriez vous en tirer sans les avoir écrites en premier lieu.
N'y a-t-il aucun danger que l'écriture échoue en raison d'une erreur IO?
Oh, absolument. Dans un tel cas, généralement le système de fichiers entier passe en mode lecture seule, et tout est horrible. Mais cela arrive rarement, inutile de perdre les avantages de performance en général.
Des E/S tamponnées asynchrones étaient utilisées avant Linux et même avant Unix. Unix l'a eu, ainsi que toutes ses ramifications.
Voici ce que Ritchie et Thompson ont écrit dans leur article CACM The UNIX Time-Sharing System :
Pour l'utilisateur, la lecture et l'écriture des fichiers semblent synchrones et sans tampon. C'est immédiatement après le retour d'un appel de lecture que les données sont disponibles, et inversement après une écriture, l'espace de travail de l'utilisateur peut être réutilisé. En fait, le système maintient un mécanisme de mise en mémoire tampon assez compliqué qui réduit considérablement le nombre d'opérations d'E/S nécessaires pour accéder à un fichier.
Dans votre question, vous avez également écrit:
N'y a-t-il aucun danger que l'écriture échoue en raison d'une erreur IO?
Oui, l'écriture peut échouer et le programme pourrait ne jamais le savoir. Bien que ce ne soit jamais une bonne chose, les effets de cela peuvent être minimisés dans les cas où une erreur d'E/S génère une panique système (sur certains systèmes d'exploitation, cela est configurable - au lieu de paniquer, le système peut continuer à fonctionner mais le système de fichiers affecté est non monté ou monté en lecture seule). Les utilisateurs peuvent alors être informés que les données sur ce système de fichiers sont suspectes. Et un lecteur de disque peut être surveillé de manière proactive pour voir si sa liste de défauts ( développée augmente rapidement, ce qui indique que le lecteur est en panne.
BSD a ajouté l'appel système fsync
afin qu'un programme puisse être certain que ses données de fichier ont été complètement écrites sur le disque avant de continuer, et les systèmes Unix suivants ont fourni des options pour effectuer des écritures synchrones. GNU dd a une option conv=fsync
pour vous assurer que toutes les données ont été écrites avant la sortie de la commande. Il est utile lors de l'écriture sur des lecteurs flash amovibles lents, où les données en mémoire tampon peuvent prendre plusieurs minutes pour être écrites.
Une autre source de corruption de fichiers est un arrêt soudain du système, par exemple à cause d'une perte d'alimentation. Pratiquement tous les systèmes actuels prennent en charge un indicateur propre/sale dans leurs systèmes de fichiers. L'indicateur est défini sur propre lorsqu'il n'y a plus de données à écrire et que le système de fichiers est sur le point d'être démonté, généralement lors de l'arrêt du système ou manuellement appelant umount
. Les systèmes exécuteront généralement fsck
au redémarrage s'ils détectent que les systèmes de fichiers n'ont pas été correctement arrêtés.
Beaucoup de bonnes réponses, mais permettez-moi d'ajouter une autre chose ... Rappelez-vous qu'Unix est un système multi-processus et multi-utilisateurs, donc potentiellement de nombreux utilisateurs essaieraient de faire des opérations sur les fichiers (en particulier les écritures) à (presque) la en même temps. Avec les vieux disques durs lents - peut-être montés sur le réseau - cela prendrait non seulement du temps (pour lequel les programmes se bloqueraient et les utilisateurs devraient attendre), mais causerait beaucoup de déplacement de la tête de lecture/écriture du disque d'avant en arrière.
Donc, à la place, les fichiers en attente d'écriture ont été conservés en mémoire pendant un certain temps, et triés après l'endroit où ils devraient se retrouver sur le disque ... et quand le tampon était plein - ou le disque - le démon de synchronisation avait attendu le nombre de secondes requis (je pense que c'était généralement environ 30 secondes) - le tampon entier a été écrit sur le disque "dans l'ordre", la tête d'écriture n'ayant qu'un seul mouvement de balayage continu, écrire les fichiers sur le disque au fur et à mesure ... au lieu de sauter partout.
Bien sûr, avec les disques rapides d'aujourd'hui - sans parler des appareils à semi-conducteurs - le gain est beaucoup moins ... en particulier sur un système Linux domestique, où il n'y a qu'un seul utilisateur travaillant à la fois, et seulement avec quelques programmes.
Quoi qu'il en soit, la combinaison de l'anticipation des lectures en lisant (dans le cache/tampon) plus que ce qui était demandé - et en triant les données en attente d'écriture, afin qu'elles puissent être écrites en "un seul mouvement" - était en fait une très bonne idée au temps, en particulier sur les systèmes avec beaucoup de lecture et d'écriture par de nombreux utilisateurs.
Il n'est pas spécifique à Linux et s'appelle page cache (ce que Linux fait assez bien). Voir aussi http://linuxatemyram.com/ ; donc si un fichier est écrit, puis relu quelques secondes plus tard, très souvent aucune E/S disque n'est nécessaire.
Le principal avantage est que sur de nombreux systèmes, il y a beaucoup de RAM, et une partie peut être utilisée comme cache par le noyau. Certaines opérations de fichiers peuvent donc profiter de cette mise en cache. En outre, le temps d'E/S disque est beaucoup plus lent (généralement plusieurs milliers de fois pour le SDD, et près d'un million de fois plus lent pour les disques durs mécaniques) que la RAM.
Le code d'application peut donner des indications concernant cette mise en cache: voir par exemple posix_fadvise (2) & madvise (2)
Les plateaux tournants sont plus lents que la RAM. Nous utilisons la mise en cache des lectures/écritures pour "masquer" ce fait.
L'utilité de l'écriture IO est qu'elle ne nécessite pas de disque IO pour se produire immédiatement - contrairement à une lecture, où vous ne pouvez pas renvoyer de données à la jusqu'à ce que la lecture se termine sur le disque.
Ainsi, les écritures fonctionnent sous une contrainte de temps douce - tant que notre débit soutenu ne dépasse pas celui de notre disque, nous pouvons masquer une grande partie des pénalités de performances dans un cache d'écriture.
Et nous avons besoin d'écrire dans le cache - les disques en rotation sont très lents comparativement. Mais pour cela, les types de RAID modernes ont une pénalité de fonctionnement importante.
Un RAID 6 par exemple, pour terminer une écriture IO doit:
Ainsi, chaque écriture est en fait 6 IO - et en particulier lorsque vous avez des disques lents comme de gros disques SATA, cela devient extrêmement coûteux.
Mais il existe une solution simple et agréable: l'écriture coalescente. Si vous pouvez créer une écriture "pleine bande" dans un tampon, vous n'avez pas besoin de lire la parité de votre disque - vous pouvez la calculer en fonction de ce que vous avez en mémoire.
Il est très souhaitable de le faire, car vous n'avez plus d'amplification d'écriture. En effet, vous pouvez vous retrouver avec une pénalité en écriture inférieure à RAID 1 + 0.
Considérer:
RAID 6, 8 + 2 - 10 broches.
8 blocs de données consécutifs à écrire - calculer la parité dans le cache et écrire un bloc sur chaque disque. 10 écritures par 8, signifie une pénalité en écriture de 1,25. 10 disques de RAID 1 + 0 ont toujours une pénalité d'écriture de 2 (car vous devez écrire dans chaque sous-miroir). Donc, dans ce scénario, vous pouvez réellement faire en sorte que RAID 6 fonctionne mieux que RAID1 + 0. En utilisation réelle, vous obtenez un peu plus d'un profil IO mixte cependant.
La mise en cache de l'écriture fait donc une énorme différence dans les performances perçues des ensembles RAID - vous pouvez écrire à RAM vitesse et avoir une faible pénalité en écriture), améliorant votre débit soutenu si vous le faites.
Et si vous ne le faites pas, vous souffrez de la lenteur des performances de SATA, mais multipliez-la par 6 et ajoutez un peu de contention. Votre RAID SATA 10 voies sans mise en cache d'écriture serait un peu plus rapide qu'un seul disque sans RAID ... mais pas beaucoup.
Cependant, comme vous le constatez, vous prenez un risque: une perte de puissance signifie une perte de données. Vous pouvez atténuer cela en effectuant des cycles de vidage du cache, en sauvegardant la batterie dans votre cache ou en utilisant un SSD ou d'autres caches non volatils.
Aucune des autres réponses mentionnées allocation retardée . XFS, ext4, BTRFS et ZFS l'utilisent tous. XFS l'utilise depuis avant qu'ext4 n'existe, je vais donc l'utiliser comme exemple:
XFS ne décide même pas où mettre les données avant l'écriture. Delayed-allocation donne à l'allocateur beaucoup plus d'informations sur lesquelles baser ses décisions. Lorsqu'un fichier est écrit pour la première fois, il n'y a aucun moyen de savoir s'il s'agira d'un fichier 4k ou d'un fichier 1G et toujours en croissance. S'il y a 10G d'espace libre contigu quelque part, mettre le fichier 4k au début ne sert à rien. Placer le gros fichier au début d'un grand espace libre réduit la fragmentation.
Toutes les autres réponses ici sont au minimum généralement correctes pour le cas normal, et je recommanderais de lire l'une d'entre elles avant la mienne, mais vous avez mentionné que dd et dd ont un cas d'utilisation typique qui peut ne pas impliquer de mise en cache d'écriture. La mise en cache en écriture est principalement implémentée au niveau du système de fichiers. Les périphériques bruts ne font normalement pas de mise en cache d'écriture (plusieurs pilotes de périphériques tels que raid ou lvm sont une autre boule de cire). Étant donné que dd est souvent utilisé avec des périphériques de blocs bruts, il fournit les options bs et connexes pour permettre des écritures de grande taille pour de meilleures performances sur les périphériques bruts. Ce n'est pas aussi utile lorsque les deux points de terminaison sont des fichiers normaux (bien que les écritures volumineuses utilisent moins d'appels système dans ce cas). L'autre endroit commun où cela est particulièrement visible est avec le package mtools qui est une implémentation du système de fichiers fat de l'espace utilisateur. l'utilisation de mtools avec un lecteur de disquette est toujours incroyablement lente car les outils sont complètement synchrones et les lecteurs de disquette sont incroyablement lents. Le montage de la disquette et l'utilisation du système de fichiers gras du noyau sont beaucoup plus réactifs, sauf pour umount qui est synchrone (et très important pour qu'il en soit ainsi afin d'éviter la perte de données, en particulier pour les périphériques amovibles comme les disquettes). Il n'y a que quelques autres programmes que je connais régulièrement utilisés avec des périphériques bruts comme des bases de données spécialement configurées (qui implémentent leur propre cache d'écriture), tar, et des outils de périphériques et de systèmes de fichiers spécialisés comme chdsk, mkfs et mt.
La philosophie n'est pas sûre par défaut.
Il existe deux stratégies raisonnables et évidentes: vider les écritures sur le disque immédiatement ou retarder l'écriture. UNIX a historiquement choisi ce dernier. Donc, soyez sûr, vous devez appeler fsync
après.
Cependant, vous pouvez spécifier la sécurité à l'avance en montant un périphérique avec l'option sync
, ou par fichier en les ouvrant avec O_SYNC
.
N'oubliez pas qu'UNIX a été conçu pour les experts en informatique. "Safe by default" n'était pas une considération. La sécurité signifie des E/S plus lentes, et ces premiers systèmes avaient vraiment des E/S lentes, ce qui rend le prix élevé. Malheureusement, ni UNIX ni Linux ne sont passés à Safe-be-default, même s'il s'agit d'un changement incessant.
Il échange une petite quantité de fiabilité pour une grande augmentation du débit.
Supposons, par exemple, un programme de compression vidéo. Avec écriture différée ("réécriture"):
Versus
La deuxième version apparaît deux fois plus vite car elle peut utiliser le CPU et le disque en même temps, tandis que la première version attend toujours l'un ou l'autre.
En règle générale, vous souhaitez une réécriture pour les opérations de streaming et les opérations de fichiers en masse, et une réécriture pour les bases de données et les applications de type base de données.
Dans de nombreuses applications, les périphériques de stockage seront occupés par intermittence à lire des données. Si un système est toujours en mesure de différer les écritures jusqu'à un moment où le périphérique de stockage n'est pas occupé à lire des données, du point de vue d'une application, les écritures ne prendront aucun temps. Les seules situations dans lesquelles l'écriture ne serait pas instantanée seraient lorsque:
Les tampons d'écriture se remplissent au point qu'aucune autre demande d'écriture différée ne peut être acceptée jusqu'à ce que les écritures soient réellement terminées.
Il est nécessaire d'arrêter ou de supprimer le périphérique pour lequel des écritures sont en attente.
Une application demande spécifiquement la confirmation qu'une écriture est réellement terminée.
En effet, c'est uniquement en raison des exigences ci-dessus que l'écriture doit jamais avoir lieu. D'un autre côté, il n'y a généralement aucune raison de ne pas effectuer d'écritures en attente à des moments où un périphérique serait autrement inactif, donc de nombreux systèmes les exécutent alors.
Il y a aussi ceci:
Écrivez "Salut, Joe Moe"
est plus rapide que:
Écrivez "Salut"
Écrivez "Joe"
Écrivez "Moe"
Et aussi:
Écrivez "Salut, comment allez-vous?"
est plus rapide que:
Écrivez "Salut, quoi de neuf?"
Supprimer cela
Écrivez "Salut, comment allez-vous?"
Supprimer cela
Écrivez "Salut, comment allez-vous?"
Il est préférable que les modifications et l'agrégation se produisent dans RAM que sur le disque. La création d'écritures sur disque libère les développeurs d'applications de ces problèmes.