Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

Avec un système de virtualisation comme User Mode Linux, chaque machine virtuelle possède son propre disque – généralement sous la forme d'un fichier image. Or, une image typique fait de quelques centaines de méga-octets à plusieurs giga-octets. Même si aujourd'hui l'espace disque est relativement bon marché, il n'en reste pas moins que la consommation augmente vite. Et c'est d'autant plus dommage qu'une bonne partie de cet espace est en réalité gaspillé: En effet, il n'est pas rare que plusieurs images aient en commun une part importante de leur contenu. Ne serait-ce que le système de base...

Pour palier à cet inconvénient, UML propose un mécanisme de copie en écriture (Copy On WriteCOW). Avec ce système, une image peut être partagée par plusieurs machines virtuelles, et seules les modifications (les écritures) qui les distinguent sont stockées dans le fichier COW spécifiques la machine.

Copy On Write.png

Copy on Write — Le mécanisme de copy on write (copie en écriture) s'apparente au principe du calque: on peut voir l'original (le backing file) au travers, mais en cas d'écriture, c'est sur le calque (le cow file) qu'on écrit. Ce mécanisme permet de supprimer les données redondantes et autorise les modifications dans chaque instance sans risque qu'elles n'interférent les unes avec les autres.

Avantages

Les avantages d'un mécanisme de Copy On Write sont multiples. Le plus évident est d'éviter de dupliquer l'original pour chaque instance. C'est particulièrement judicieux dans le cas d'un système de virtualisation, puisque les fichiers image utilisés par différentes machines virtuelles sont pour l'essentiel identiques. Et d'une machine virtuelle à l'autre, la majorité des fichiers reste inchangée.

COW et sparse file

Pour être vraiment efficaces en terme d'économie de place, les fichiers COW doivent être des fichiers creux (sparse files).

En effet, pour des raisons d'efficacité, le fichier COW occupe la même taille logique que le fichier original. Autrement dit, un fichier COW typique contient beaucoup d'espace vide (de trous au travers desquels on voit l'original). Et il est donc inutile de réellement allouer de l'espace disque pour tous ces secteurs vides.

Si je mets en avant l'économie de place, celle-ci a un corollaire moins visible: une amélioration des performances globales si plusieurs machines virtuelles utilisent les mêmes backing files. Pourquoi? Parce que cela réduit le nombre de chargements nécessaires quand plusieurs machines virtuelles font appel aux mêmes données.

Un exemple sera plus simple à comprendre: imaginons deux machines virtuelles qui lancent chacune un shell. Si elles ont chacune leur propre image disque, les secteurs contenant le code du shell vont devoir être lus et chargés en mémoire individuellement. A l'inverse, si les deux machines partagent la même image, la première machine utilisant le shell va effectivement déclencher la lecture des secteurs correspondant dans le fichier image. Mais quand la seconde machine chargera son shell, les secteurs correspondants étant déjà présents en mémoire principale, ils n'auront pas à être relus: non seulement on évite un chargement, mais en plus l'espace mémoire occupé sera deux fois moins important.

En pratique

Pour associer un fichier COW à une instance de machine virtuelle UML, il suffit de l'indiquer dans le paramètre qui désigne l'image disque à charger. Comme vous le verrez dans l'exemple ci-dessous, le fichier COW est indiqué juste avant le fichier image original, et les deux sont séparés par une virgule:

sh$ linux ubda=machine1.cow,lenny-image \
            con=pts  con0=fd:0,fd:1 umid=uml

Si le fichier COW n'existe pas, il est créé. Dans tous les cas, les modifications qui seront effectuées sur le système de fichier seront écrites dans le fichier COW – jamais dans l'original. Vous pouvez vérifier que le fichier a bien été créé sur l'hôte (et que c'est un fichier creux) à l'aide de la commande ls:

sh$ ls -ls
total 253444
252412 -rw-r--r-- 1 sylvain sylvain 300000000 2010-03-29 11:42 lenny-image
  1032 -rw-r--r-- 1 sylvain sylvain 300081920 2010-03-29 11:45 machine1.cow

Et bien sûr, vous pouvez lancer plusieurs machines virtuelles à partir de la même image originale. A condition d'utiliser un fichier COW différent pour chacune:

sh$ linux ubda=machine2.cow,lenny-image \
            con=pts  con0=fd:0,fd:1

Et sur l'hôte vous verrez un second fichier COW créé:

sh$ ls -ls
total 253476
252412 -rw-r--r-- 1 sylvain sylvain 300000000 2010-03-29 11:42 lenny-image
  1032 -rw-r--r-- 1 sylvain sylvain 300081920 2010-03-29 11:45 machine1.cow
    32 -rw-r--r-- 1 sylvain sylvain 300081920 2010-03-29 11:55 machine2.cow

Remarque:

La différence entre le nombre de secteurs réellement occupés par les deux fichiers COW est liée au fait que j'ai un peu joué avec la première machine: même sans modifier volontairement des données, il est vraisemblable que des fichiers de log aient été modifiés, ce qui explique cette différence.

Avertissement

Bon, je n'ai plus grand chose à dire sur ce sujet. Si ce n'est un important avertissement: pour fonctionner normalement, les fichiers COW nécessitent que l'original ne soit plus modifié. Plus jamais. Autrement dit, par exemple, pas question de vouloir faire une mise à jour sur l'original en espérant mettre à jour toutes les machines virtuelles. Ça serait certes pratique. Mais ça ne marcherait pas. La seule solution ici serait de mettre à jour individuellement chaque machine virtuelle – et donc de faire grossir chaque fichier COW en dupliquant des données – et par là même perdre une partie des bénéfices liés à ce mécanisme...

Piège:

C'est tellement important que les originaux ne soient pas modifiés, que UML stocke dans le fichier COW la taille et la date de modification de l'original, pour s'assurer au chargement qu'il n'a pas été modifié:

sh$ touch lenny-image
sh$ linux ubda=machine2.cow,lenny-image \
            con=pts  con0=fd:0,fd:1
[...]
mtime mismatch (1269855720 vs 1269857385) of COW header vs backing file
Failed to open 'machine2.cow', errno = 22
ubda: Can't open "machine2.cow": errno = 22
[...]
Segmentation fault

Le message d'erreur ci-dessus indique la date attendue (en secondes depuis le 1er janvier 1970 – ce qui va me permettre de réparer mon erreur:

sh$ touch --date='1970-01-01 UTC 1269855720 seconds' lenny-image
sh$ linux ubda=machine2.cow,lenny-image \
            con=pts  con0=fd:0,fd:1
uml login: 

Ceci-dit, cette manipulation a été un succès car le fichier n'avait pas réellement été modifié. Seule la date de modification avait changé. C'est ce qui se produirait par exemple si vous copiez le fichier sans prendre garde à préserver la date de modification (en oubliant l'option -p de la commande cp).

Conclusion

Je ne vais pas trop m'éterniser: vous l'avez compris, les fichiers COW, c'est vachement bien. Donc, inutile de s'en priver, surtout si vous souhaitez faire coexister plusieurs instances de machines virtuelles basées sur la même image disque.

Par ailleurs, même si vous avez l'intention de ne faire tourner qu'une seule machine, le fichier COW peu aussi vous servir de brouillon: puisque les modifications sont faites sans toucher à l'original, vous n'avez plus à craindre de casser votre image. En cas de gros problème, supprimez simplement le fichier COW, et toutes vos bêtises ont disparu. Si vous aimez ce mode de fonctionnement (qui s'apparente au fonctionnement d'un système de gestion de version comme cvs ou svn), je vous engage à jeter un oeil à la commande uml_moo qui permet de fusionner dans l'original les modifications apportées dans un fichier COW. L'équivalent d'un commit, quoi...

Référence