Si je regarde le contenu de sleepimage ou swapfile dans /var/vm
, je constate que ce ne sont que 1,07 Go d'octets nuls. Je m'attendrais à ce qu'ils contiennent des données réelles, ou s'ils ne sont pas utilisés, à être un fichier de 0 o. J'ai vérifié quelques versions de système d'exploitation différents et j'ai vérifié que c'est le cas de la version 10.9 à la version 11.6, donc je doute que cela soit quelque chose de particulier à une configuration ou un système de fichiers donné.
Réponse
Trop de publicités?J'ai décidé de creuser dans le code source du noyau et j'ai une réponse approximative, mais ce n'est toujours pas 100% clair. Si vous regardez IOHibernateIO.cpp
à l'intérieur de la méthode IOHibernateSystemPostWake
, vous verrez l'appel suivant (note : légèrement différent pour 10.11+, mais la même fonction finit par être appelée)
if (kFSOpened == gFSState)
{
// invalider et fermer le fichier image
gIOHibernateCurrentHeader->signature = kIOHibernateHeaderInvalidSignature;
if ((fileRef = gIOHibernateFileRef))
{
gIOHibernateFileRef = 0;
IOSleep(TRIM_DELAY);
kern_close_file_for_direct_io(fileRef,
#if DISABLE_TRIM
0, 0, 0, 0, 0);
#else
0, (caddr_t) gIOHibernateCurrentHeader,
sizeof(IOHibernateImageHeader),
0,
gIOHibernateCurrentHeader->imageSize);
#endif
}
gFSState = kFSIdle;
}
return (kIOReturnSuccess);
De manière importante, DISABLE_TRIM
est défini comme 0, donc nous finissons par appeler kern_close_file_for_direct_io
dont la signature est la suivante :
void
kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t * ref,
off_t write_offset, caddr_t addr, vm_size_t write_length,
off_t discard_offset, off_t discard_end)
et dont l'implémentation semble d'abord émettre un DKIOCUNMAP
ioctl pour les étendues appartenant au fichier d'hibernation, puis copie write_length
octets depuis l'adresse mémoire addr
dans le fichier. Si nous regardons un peu en arrière, gIOHibernateCurrentHeader
est mis à zéro en mémoire après que l'image d'hibernation soit écrite pendant IOHibernateSystemSleep
.
Cela conduit donc à quelques conclusions, ainsi qu'à quelques questions persistantes :
-
Cela semble en effet être un comportement intentionnel, et je peux voir un
DKIOCUNMAP
similaire être émis à l'intérieur devm_compressor_backing_file.c
, donc je suppose que quelque chose de similaire se produit également pour les fichiers d'échange. -
Cependant, je ne suis pas certain de ce que cela signifie pour l'espace réellement pris sur le disque. Pour APFS, c'est trivialement explicite car il prend en charge les fichiers peu denses (donc même si le fichier est en fait 1,7 Go d'octets nuls, cela n'aura pas d'importance), mais que se passe-t-il pour HFS+ ? Ma compréhension est que le ioctl
DKIOCUNMAP
se traduit en fait en commande SSD TRIM pour nettoyer les blocs utilisés :
La méthode doUnmap a été introduite en remplacement de la méthode doDiscard. Elle effectue une fonction similaire, qui est de libérer les blocs de disque qui ne sont pas utilisés par le système de fichiers. Contrairement à la méthode doDiscard, qui est capable de libérer uniquement une seule séquence de blocs de disque physiquement contigus, la méthode doUnmap reçoit un tableau contenant une ou plusieurs plages de blocs de disque qui ne sont plus utilisées. Un processus d'espace utilisateur peut effectuer cette action en envoyant le ioctl DKIOCUNMAP.
ce qui semblerait indiquer qu'en dépit de la pensée au niveau du système de fichiers que la taille du fichier est de 1,7 Go, aucune de ces étendues ne pointe effectivement vers des blocs valides sur le disque, et donc la taille physique du fichier est bien moins importante. Cela expliquerait également pourquoi il y a 1,7 Go de zéros, puisque je suppose que le contrôleur SSD, lorsqu'il reçoit la commande de trim, pourrait implanter RZAT (retourner des zéros après trim). Cette hypothèse pourrait être testée en essayant cela sur un disque dur à plateaux et en vérifiant si des données sont renvoyées.