Man page - userfaultfd(2)

Packages contains this manual

Available languages:

en fr

Manual

userfaultfd

NOM
BIBLIOTHÈQUE
SYNOPSIS
DESCRIPTION
Utilisation
Fonctionnement d’userfaultfd
Mode protection d’écriture d’userfaultfd (depuis Linux 5.7)
Mode erreur mineure d’userfaultfd (depuis Linux 5.13)
Lire Ă  partir de la structure userfaultfd
VALEUR RENVOYÉE
ERREURS
STANDARDS
HISTORIQUE
NOTES
BOGUES
EXEMPLES
Source du programme
VOIR AUSSI
TRADUCTION

NOM

userfaultfd - Créer un descripteur de fichier pour gérer les erreurs de page en espace utilisateur

BIBLIOTHÈQUE

BibliothĂšque C standard ( libc , -lc )

SYNOPSIS

#include <fcntl.h> /* Définition des constantes O_* */
#include <sys/syscall.h>
/* Définition des constantes SYS_* */
#include <linux/userfaultfd.h>
/* Définition des constantes UFFD_* */
#include <unistd.h>

int syscall(SYS_userfaultfd, int flags );

Note : la glibc ne fournit pas d’enveloppe pour userfaultfd (), imposant l’utilisation de syscall (2).

DESCRIPTION

userfaultfd () crĂ©e un nouvel objet userfaultfd qui peut ĂȘtre utilisĂ© pour la dĂ©lĂ©gation de la gestion des erreurs de page Ă  une application de l’espace utilisateur et renvoie un descripteur de fichier qui fait rĂ©fĂ©rence au nouvel objet. Le nouvel objet userfaultfd est configurĂ© en utilisant ioctl (2).

Une fois l’objet userfaultfd configurĂ©, l’application peut utiliser read (2) pour recevoir des notification d’userfaultfd. Les lectures Ă  partir d’userfaultfd peuvent ĂȘtre bloquantes ou non bloquantes en fonction de la valeur des attributs ( flags ) utilisĂ©s pour la crĂ©ation de l’userfaultfd ou des appels suivants Ă  fcntl (2).

Les valeurs suivantes peuvent ĂȘtre combinĂ©es dans flags par un OU binaire pour modifier le comportement d’ userfaultfd () :
O_CLOEXEC

Activer l’attribut close-on-exec pour le nouveau descripteur de fichier userfaultfd. Consultez la description de l’attribut O_CLOEXEC dans open (2).

O_NONBLOCK

Permettre une opĂ©ration non bloquante pour l’objet userfaultfd. Voir la description de l’attribut O_NONBLOCK dans open (2).

UFFD_USER_MODE_ONLY

C’est un attribut spĂ©cifique Ă  userfaultfd qui a Ă©tĂ© introduit dans Linux 5.11. Quand il est dĂ©fini, l’objet userfaultfd ne pourra gĂ©rer que les erreurs de page provenant de l’espace utilisateur dans les rĂ©gions enregistrĂ©es. Quand une erreur provenant du noyau est dĂ©clenchĂ©e dans l’intervalle enregistrĂ© avec cet userfaultfd, un signal SIGBUS sera envoyĂ©.

Quand le dernier descripteur de fichier faisant rĂ©fĂ©rence Ă  un objet userfaultfd est fermĂ©, tous les intervalles de mĂ©moire qui ont Ă©tĂ© enregistrĂ©s avec l’objet sont dĂ©senregistrĂ©s et les Ă©vĂ©nements non lus sont vidĂ©s.

Userfaultfd gùre trois modes d’enregistrement :
UFFDIO_REGISTER_MODE_MISSING
(depuis Linux 4.10)

Quand il est enregistrĂ© avec le mode UFFDIO_REGISTER_MODE_MISSING , l’espace utilisateur recevra une notification d’erreur de page lors de l’accĂšs Ă  une page manquante. L’exĂ©cution du thread fautif sera arrĂȘtĂ©e jusqu’à ce que l’erreur de page soit rĂ©solue Ă  partir de l’espace utilisateur par un ioctl UFFDIO_COPY ou UFFDIO_ZEROPAGE .

UFFDIO_REGISTER_MODE_MINOR (depuis Linux 5.13)

Quand il est enregistrĂ© avec le mode UFFDIO_REGISTER_MODE_MINOR , l’espace utilisateur recevra une notification d’erreur de page lorsqu’une erreur de page mineure survient. C’est-Ă -dire quand une page de sauvegarde est dans le cache de page, mais les entrĂ©es dans la table de pages n’existent pas encore. L’exĂ©cution du thread fautif sera arrĂȘtĂ©e jusqu’à ce que l’erreur de page soit rĂ©solue Ă  partir de l’espace utilisateur par un ioctl UFFDIO_CONTINUE .

UFFDIO_REGISTER_MODE_WP (depuis Linux 5.7)

Quand il est enregistrĂ© avec le mode UFFDIO_REGISTER_MODE_WP , l’espace utilisateur recevra une notification d’erreur de page lors d’une Ă©criture sur une page protĂ©gĂ©e en Ă©criture. L’exĂ©cution du thread fautif sera arrĂȘtĂ©e jusqu’à ce que l’espace utilisateur supprime la protection de la page en utilisant un ioctl UFFDIO_WRITEPROTECT .

Plusieurs modes peuvent ĂȘtre activĂ©s en mĂȘme temps pour le mĂȘme intervalle de mĂ©moire.

Depuis Linux 4.14, une notification d’erreur de page d’userfaultfd peut incorporer de façon sĂ©lective des informations d’identifiant des threads en erreur dans une notification. Il est nĂ©cessaire d’activer cette fonctionnalitĂ© explicitement en utilisant le bit de fonction UFFD_FEATURE_THREAD_ID lors de l’initialisation du contexte d’userfaultfd. Par dĂ©faut, la dĂ©claration de l’identifiant du thread est dĂ©sactivĂ©e.

Utilisation

Le mĂ©canisme d’userfaultfd est conçu pour permettre Ă  un thread dans un programme multi-thread de rĂ©aliser la pagination en espace utilisateur pour d’autres threads dans le processus. Lorsqu’un erreur de page survient pour une des rĂ©gions enregistrĂ©es dans l’objet userfaultfd, le thread en erreur est mis en sommeil et un Ă©vĂ©nement est gĂ©nĂ©rĂ© qui peut ĂȘtre lu au moyen du descripteur de fichier userfaultfd. Le thread de gestion d’erreur lit les Ă©vĂ©nements Ă  partir de ce descripteur de fichier et les corrige en utilisant les opĂ©rations dĂ©crites dans ioctl_userfaultfd (2). Lors de l’intervention sur les Ă©vĂ©nements d’erreur de page, le thread de gestion d’erreur peut dĂ©clencher le rĂ©veil d’un thread endormi.

Il est possible que les threads en erreur et les threads traitant les erreurs soient exĂ©cutĂ©s dans le contexte de processus diffĂ©rents. Dans ce cas, ces threads peuvent appartenir Ă  diffĂ©rents programmes, et le programme qui exĂ©cute les threads en erreur ne collaborera pas nĂ©cessairement avec le programme qui gĂšre les erreurs de page. Dans ce mode non coopĂ©ratif, le processus qui contrĂŽle userfaultfd et gĂšre les erreurs de page a besoin d’avoir connaissance des modifications dans la disposition de la mĂ©moire virtuelle du processus en erreur pour Ă©viter une corruption de mĂ©moire.’

Depuis Linux 4.11, userfaultfd peut aussi informer les threads gĂ©rant les erreurs des modifications dans la disposition de la mĂ©moire virtuelle du processus en erreur. De plus, si le processus en erreur invoque fork (2), les objets userfaultfd associĂ©s au parent peuvent ĂȘtre dupliquĂ©s dans le processus enfant et le contrĂŽleur d’userfaultfd sera informĂ© (au moyen de UFFD_EVENT_FORK dĂ©crit plus bas) sur le descripteur de fichier associĂ© aux objets userfault créés pour le processus enfant, ce qui permet au contrĂŽleur d’userfaultfd de rĂ©aliser la pagination de l’espace utilisateur pour le processus enfant. À la diffĂ©rence des erreurs de page qui doivent ĂȘtre synchrones et rĂ©clament un rĂ©veil explicite ou explicite, tous les autres Ă©vĂ©nements sont envoyĂ©s de façon asynchrone et le processus non coopĂ©ratif reprend son exĂ©cution dĂšs que le gestionnaire d’userfaultfd exĂ©cute read (2). Le gestionnaire d’userfaultfd doit soigneusement synchroniser les appels Ă  UFFDIO_COPY avec le traitement des Ă©vĂ©nements.

Le modĂšle asynchrone actuel d’envoi d’évĂ©nement est optimal pour des implĂ©mentations de gestionnaire userfaultfd non coopĂ©ratif Ă  thread unique.

Depuis Linux 5.7, userfaultfd peut effectuer le suivi synchrone de page sale en utilisant le nouveau mode d’enregistrement de page protĂ©gĂ©e en Ă©criture. Il faut vĂ©rifier le bit de fonction UFFD_FEATURE_PAGEFAULT_FLAG_WP avant d’utiliser cette fonctionnalitĂ©. Le mode protection en Ă©criture, similaire au mode d’origine page manquante d’userfaultfd, gĂ©nĂšre une notification d’userfaultfd quand la page protĂ©gĂ©e en Ă©criture est Ă©crite. L’utilisateur doit rĂ©soudre l’erreur de page en dĂ©protĂ©geant la page fautive et en forçant le thread fautif Ă  continuer. Pour plus d’informations, consultez la section « Mode protection d’écriture d’userfaultfd »

Fonctionnement d’userfaultfd

AprĂšs la crĂ©ation de l’objet userfaultfd avec userfaultfd (), l’application doit l’activer en utilisant l’opĂ©ration UFFDIO_API de ioctl (2). Cette opĂ©ration permet une connexion en deux Ă©tapes entre le noyau et l’espace utilisateur pour dĂ©terminer quelle version de l’API et quelles fonctions sont prises en charge par le noyau. et ensuite pour activer les fonctions voulues par l’espace utilisateur. Cette opĂ©ration doit ĂȘtre rĂ©alisĂ©e avant toutes les autres opĂ©rations ioctl (2) dĂ©crites plus bas (ou ces opĂ©rations Ă©chouent avec l’erreur EINVAL .)

AprĂšs le succĂšs d’une opĂ©ration UFFDIO_API , l’application enregistre alors les intervalles d’adresses mĂ©moire en utilisant l’opĂ©ration d’ ioctl (2) UFFDIO_REGISTER . Quand l’opĂ©ration UFFDIO_REGISTER s’est achevĂ©e avec succĂšs, une erreur de page, se produisant dans l’intervalle de mĂ©moire requis et satisfaisant au mode dĂ©fini au moment de l’enregistrement, sera transmis par le noyau Ă  l’application de l’espace utilisateur. L’application peut alors utiliser diverses opĂ©rations d’ ioctl (2) (parexemple, UFFDIO_COPY , UFFDIO_ZEROPAGE ou UFFDIO_CONTINUE ) pour rĂ©soudre l’erreur de page.

Depuis Linux 4.4, si l’application dĂ©finit le bit de la fonction UFFD_FEATURE_SIGBUS en utilisant l’ ioctl (2) UFFDIO_API , aucune notification d’erreur d page ne sera transmise Ă  l’espace utilisateur. Un signal est envoyĂ© Ă  la place au processus en erreur. Avec cette fonction, userfaultfd peut ĂȘtre utilisĂ© Ă  des fins de robustesse pour capturer simplement tout accĂšs aux zones dans l’intervalle d’adresses enregistrĂ© qui n’ont pas de pages allouĂ©es sans avoir Ă  Ă©couter les Ă©vĂ©nements d’userfaultfd. Aucun contrĂŽleur d’userfaultfd ne sera requis pour traiter ce type d’accĂšs mĂ©moire. Par exemple, cette fonction peut ĂȘtre utile Ă  des applications qui dĂ©sirent empĂȘcher le noyau d’allouer des pages automatiquement et de remplir des trous dans des fichiers creux quand c’est un mappage mĂ©moire qui permet l’accĂšs aux trous.

La fonction UFFD_FEATURE_SIGBUS est héritée de façon implicite avec fork (2) si elle est utilisée en combinaison avec UFFD_FEATURE_FORK .

Des dĂ©tails sur les diffĂ©rentes opĂ©rations d’ ioctl (2) sont disponibles dans ioctl_userfaultfd (2).

Depuis Linux 4.11, les Ă©vĂ©nements autres que les erreurs de page peuvent ĂȘtre activĂ©s pendant l’opĂ©ration UFFDIO_API .

Jusqu’à Linux 4.11, userfaultfd ne peut ĂȘtre utilisĂ© qu’avec des mappages de mĂ©moire privĂ©e anonyme. Depuis Linux 4.11, userfaultfd peut aussi ĂȘtre utilisĂ© avec des mappages de mĂ©moire hugelbfs et partagĂ©e.

Mode protection d’écriture d’userfaultfd (depuis Linux 5.7)

Depuis Linux 5.7, userfaultfd prend en charge le mode protection d’écriture pour la mĂ©moire anonyme. L’utilisateur doit d’abord vĂ©rifier la disponibilitĂ© de cette fonctionnalitĂ© en utilisant l’ioctl UFFDIO_API sur le bit de fonction UFFD_FEATURE_PAGEFAULT_FLAG_WP avant d’utiliser cette fonctionnalitĂ©.

Depuis Linux 5.19, le mode protection d’écriture est aussi pris en charge sur la mĂ©moire de type shmem ou hugetlbfs. Il peut ĂȘtre dĂ©tectĂ© avec le bit de fonction UFFD_FEATURE_WP_HUGETLBFS_SHMEM .

Pour enregistrer avec le mode page protĂ©gĂ©e en Ă©criture de userfaultfd, l’utilisateur doit initier l’ioctl UFFDIO_REGISTER avec le mode UFFDIO_REGISTER_MODE_WP dĂ©fini. Notez qu’il est permis de surveiller le mĂȘme intervalle de mĂ©moire avec plusieurs modes. Par exemple, un utilisateur peut effectuer UFFDIO_REGISTER avec le mode dĂ©fini Ă  UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP . Quand seul le mode UFFDIO_REGISTER_MODE_WP est enregistrĂ©, l’espace utilisateur ne recevra aucune notification quand une page manquante est Ă©crite. À la place, l’espace utilisateur ne recevra une notification d’erreur de page protĂ©gĂ©e en Ă©criture que quand une page existante et protĂ©gĂ©e en Ă©criture est Ă©crite.

AprĂšs que l’ioctl UFFDIO_REGISTER s’est terminĂ© avec le mode UFFDIO_REGISTER_MODE_WP dĂ©fini, l’utilisateur peut protĂ©ger en Ă©criture toute mĂ©moire dans l’intervalle en utilisant l’ioctl UFFDIO_WRITEPROTECT oĂč uffdio_writeprotect.mode devrait ĂȘtre dĂ©fini Ă  UFFDIO_WRITEPROTECT_MODE_WP .

Quand un Ă©vĂ©nement de protection en Ă©criture survient, l’espace utilisateur recevra une notification d’erreur de page dont l’ uffd_msg.pagefault.flags aura l’attribut UFFD_PAGEFAULT_FLAG_WP dĂ©fini. Notez : dans la mesure oĂč seulement les Ă©critures peuvent dĂ©clencher ce genre d’erreur, les notifications de protection en Ă©criture auront toujours le bit UFFD_PAGEFAULT_FLAG_WRITE dĂ©fini en mĂȘme temps que le bit UFFD_PAGEFAULT_FLAG_WP .

Pour rĂ©soudre une erreur de page de protection d’écriture, l’utilisateur doit initier un autre ioctl UFFDIO_WRITEPROTECT dont l’ uffd_msg.pagefault.flags doit avoir l’attribut UFFDIO_WRITEPROTECT_MODE_WP effacĂ© aprĂšs la page ou l’intervalle fautif.

Mode erreur mineure d’userfaultfd (depuis Linux 5.13)

Depuis Linux 5.13, userfaultfd prend en charge le mode erreur mineure. Dans ce mode, les messages d’erreur ne sont pas produits pour des erreurs majeures (oĂč les pages Ă©taient absentes), mais plutĂŽt pour des erreurs mineures oĂč une page existe dans le cache de page, mais oĂč les entrĂ©es de la table de pages ne sont pas encore prĂ©sentes. L’utilisateur doit d’abord vĂ©rifier la disponibilitĂ© de cette fonctionnalitĂ© en utilisant l’ioctl UFFDIO_API avec les bits de fonction appropriĂ©s avant d’utiliser cette fonctionnalité : UFFD_FEATURE_MINOR_HUGETLBFS depuis Linux 5.13 ou UFFD_FEATURE_MINOR_SHMEM depuis Linux 5.14.

Pour enregistrer avec le mode erreur mineure d’userfaultfd, l’utilisateur doit initier l’ioctl UFFDIO_REGISTER avec le mode UFFD_REGISTER_MODE_MINOR dĂ©fini.

Quand une erreur mineure survient, l’espace utilisateur recevra une notification d’erreur de page dont l’ uffd_msg.pagefault.flags aura l’attribut UFFD_PAGEFAULT_FLAG_MINOR dĂ©fini.

Pour rĂ©soudre une erreur de page mineure, le gestionnaire doit dĂ©cider si le contenu de la page existante doit ĂȘtre modifiĂ©e d’abord, ou non. Si c’est le cas, cela doit ĂȘtre fait Ă  son emplacement au moyen d’un second mappage non enregistrĂ© par userfaultfd vers la mĂȘme page de sauvegarde (par exemple en mappant deux fois le fichier shmem ou hugetlbfs). Une fois que la page est considĂ©rĂ©e « à jour », l’erreur peut ĂȘtre rĂ©solue en initiant un ioctl UFFDIO_CONTINUE qui installe les entrĂ©es de la table de pages et (par dĂ©faut) rĂ©veille le ou les threads en erreur.

Le mode erreur mineure ne prend en charge que la mĂ©moire s’appuyant sur hugetlbfs (depuis Linux 5.13) et sur shmem (depuis Linux 5.14).

Lire Ă  partir de la structure userfaultfd

Chaque read (2) Ă  partir du descripteur de fichier userfaultfd renvoie une ou plusieurs structures uffd_msg , chacune d’elles dĂ©crit un Ă©vĂ©nement d’erreur de page ou un Ă©vĂ©nement requis pour l’utilisation non coopĂ©rative d’userfaultfd :

struct uffd_msg {
__u8 event; /* Type d’évĂ©nement */
...
union {
struct {
__u64 flags; /* Attributs dĂ©crivant l’erreur */
__u64 address; /* Adresse fautive */
union {
__u32 ptid; /* ID du thread de l’erreur */
} feat;
} pagefault;
struct { /* Depuis Linux 4.11 */
__u32 ufd; /* Descripteur de ficher d’userfault
du processus enfant */
} fork;
struct { /* Depuis Linux 4.11 */
__u64 from; /* Ancienne adresse de la zone remappée */
__u64 to; /* Nouvelle adresse de la zone remappée */
__u64 len; /* Taille originale du mappage */
} remap;
struct { /* Depuis Linux 4.11 */
__u64 start; /* Adresse de début de la zone supprimée */
__u64 end; /* Adresse de fin de la zone supprimée */
} remove;
...
} arg;
/* Remplissage des champs omis */
} __packed;

Si plusieurs Ă©vĂ©nements sont disponibles et si le tampon fourni est suffisamment grand, read (2) renvoie autant d’évĂ©nements qu’il en tient dans le tampon fourni. Si le tampon fourni Ă  read (2) est plus petit que la taille de la structure uffd_msg , read (2) Ă©choue avec l’erreur EINVAL .

Les champs définis dans la structure uffd_msg sont les suivants :

event

Le type d’évĂ©nement. Selon le type d’évĂ©nement, diffĂ©rents champs de l’union arg reprĂ©sentent les dĂ©tails nĂ©cessaires au traitement de l’évĂ©nement. Les Ă©vĂ©nements qui ne sont pas des erreurs de page ne sont gĂ©nĂ©rĂ©s que quand la fonctionnalitĂ© appropriĂ©e est activĂ©e durant la connexion de l’API Ă  l’ ioctl (2) UFFDIO_API .

Les valeurs suivantes peuvent apparaĂźtre dans le champ event :
UFFD_EVENT_PAGEFAULT
(depuis Linux 4.3)

Un Ă©vĂ©nement d’erreur de page. Les dĂ©tails de l’erreur de page sont disponibles dans le champ pagefault .

UFFD_EVENT_FORK (depuis Linux 4.11)

GĂ©nĂ©rĂ© lorsque le processus en erreur invoque fork (2) (ou clone (2) sans l’attribut CLONE_VM ). Les dĂ©tails de l’évĂ©nement sont disponibles dans le champ fork .

UFFD_EVENT_REMAP (depuis Linux 4.11)

GĂ©nĂ©rĂ© lorsque le processus en erreur invoque mremap (2). Les dĂ©tails de l’évĂ©nement sont disponibles dans le champ remap .

UFFD_EVENT_REMOVE (depuis Linux 4.11)

GĂ©nĂ©rĂ© lorsque le processus en erreur invoque madvise (2) avec les conseils MADV_DONTNEED ou MADV_REMOVE . Les dĂ©tails de l’évĂ©nement sont disponibles dans le champ remove .

UFFD_EVENT_UNMAP (depuis Linux 4.11)

GĂ©nĂ©rĂ© lorsque le processus en erreur supprime le mappage d’un intervalle de mĂ©moire soit explicitement avec munmap (2), soit implicitement durant l’exĂ©cution de mmap (2) ou mremap (2). Les dĂ©tails de l’évĂ©nement sont disponibles dans le champ remove .

pagefault.address

L’adresse qui a dĂ©clenchĂ© l’erreur de page.

pagefault.flags

Un masque de bits qui dĂ©crit l’évĂ©nement. Pour UFFD_EVENT_PAGEFAULT , les attributs suivants peuvent apparaĂźtre :
UFFD_PAGEFAULT_FLAG_WP

Si cet attribut est dĂ©fini, alors l’erreur Ă©tait une erreur de protection en Ă©criture.

UFFD_PAGEFAULT_FLAG_MINOR

Si cet attribut est dĂ©fini, alors l’erreur Ă©tait une erreur mineure.

UFFD_PAGEFAULT_FLAG_WRITE

Si cet attribut est dĂ©fini, alors l’erreur Ă©tait une erreur d’écriture.

Si ni UFFD_PAGEFAULT_FLAG_WP ni UFFD_PAGEFAULT_FLAG_MINOR ne sont dĂ©finis, l’erreur Ă©tait une erreur d’absence.

pagefault.feat.pid

L’identifiant du thread qui a dĂ©clenchĂ© l’erreur de page.

fork.ufd

Le descripteur de fichier associĂ© Ă  l’objet userfault créé pour l’enfant créé par fork (2).

remap.from

L’adresse d’origine de la plage de mĂ©moire dont le mappage a Ă©tĂ© modifiĂ© en utilisant madvise (2).

remap.to

La nouvelle adresse de la plage de mémoire dont le mappage a été modifié en utilisant madvise (2).

remap.len

La taille d’origine de la plage de mĂ©moire dont le mappage a Ă©tĂ© modifiĂ© en utilisant madvise (2).

remove.start

L’adresse de dĂ©but de la plage de mĂ©moire qui a Ă©tĂ© libĂ©rĂ©e en utilisant madvise (2) ou dont le mappage a Ă©tĂ© supprimĂ©.

remove.end

L’adresse terminale de la plage de mĂ©moire qui a Ă©tĂ© libĂ©rĂ©e en utilisant madvise (2) ou dont le mappage a Ă©tĂ© supprimĂ©.

read (2) sur un descripteur de fichier userfaultfd peut échouer pour les raisons suivantes :

EINVAL

L’objet userfaultfd n’a pas encore Ă©tĂ© activĂ© avec l’opĂ©ration d’ ioctl (2) UFFDIO_API .

Si l’attribut O_NONBLOCK est activĂ© dans la description de fichier ouvert associĂ©e, le descripteur de fichier userfaultfd peut ĂȘtre surveillĂ© avec poll (2), select (2) et epoll (7). Quand les Ă©vĂ©nements sont disponibles, le descripteur de fichier l’indique comme lisible. Si l’attribut O_NONBLOCK n’est pas activĂ©, alors poll (2) indique (toujours) que le fichier comme ayant une condition POLLERR et select (2) indique que le descripteur de fichier est Ă  la fois accessible en lecture et en Ă©criture.

VALEUR RENVOYÉE

En cas de succĂšs, userfaultfd () renvoie un nouveau descripteur de fichier qui fait rĂ©fĂ©rence Ă  l’objet userfaultfd. En cas d’erreur, la fonction renvoie -1 et errno est dĂ©fini pour indiquer l’erreur.

ERREURS

EINVAL

Une valeur non prise en compte a été spécifiée dans flags .

EMFILE

La limite par processus du nombre de descripteurs de fichier ouverts a été atteinte.

ENFILE

La limite du nombre total de fichiers ouverts pour le systÚme entier a été atteinte.

ENOMEM

La mĂ©moire disponible du noyau n’était pas suffisante.

EPERM (depuis Linux 5.2)

L’appelant n’est pas privilĂ©giĂ© (il n’a pas la capacitĂ© CAP_SYS_PTRACE dans l’espace de noms initial) et /proc/sys/vm/unprivileged_userfaultfd a la valeur 0 .

STANDARDS

Linux.

HISTORIQUE

Linux 4.3.

La prise en charge des zones de mémoire hugetlbfs et partagée et des événements qui ne sont pas des erreurs de page a été ajoutée dans Linux 4.11

NOTES

Le mĂ©canisme d’userfaultfd peut ĂȘtre utilisĂ© comme une alternative aux techniques traditionnelles de pagination de l’espace utilisateur basĂ©es sur l’utilisation du signal SIGSEGV et de mmap (2). Il peut aussi ĂȘtre utilisĂ© pour implĂ©menter la restauration en mode paresseux (« lazy restore ») pour les mĂ©canismes de la fonctionnalitĂ© de gel des applications (checkpoint/restore), aussi bien que la migration aprĂšs copie pour permettre une exĂ©cution (presque) ininterrompue lors du transfert de machines virtuelles et de conteneurs Linux d’un hĂŽte Ă  un autre.

BOGUES

Si UFFD_FEATURE_EVENT_FORK est activĂ© et si un appel systĂšme issu de la famille de fork (2) est interrompu par un signal ou Ă©choue, un descripteur pĂ©rimĂ© d’userfaultfd peut ĂȘtre créé. Dans ce cas, un faux UFFD_EVENT_FORK sera fourni au surveillant d’userfaultfd.

EXEMPLES

Le programme ci-dessous dĂ©montre l’utilisation du mĂ©canisme userfaultfd. Le programme crĂ©e deux threads, un qui agit comme gestionnaire d’erreur de page pour le processus, pour les pages dans une rĂ©gion sans demande de page en utilisant mmap (2).

Le programme prend un argument en ligne de commande, qui est le nombre de pages qui seront créées dans un mappage dont les erreurs de pages seront gĂ©rĂ©es au moyen d’userfaultfd. AprĂšs la crĂ©ation d’un objet userfaultfd, le programme crĂ©e alors un mappage privĂ© anonyme de la taille spĂ©cifiĂ©e et enregistre l’intervalle d’adresses de ce mappage en utilisant l’opĂ©ration d’ ioctl (2) UFFDIO_REGISTER . Le programme crĂ©e alors un second thread qui exĂ©cutera la tĂąche de gestion des erreurs de page.

Le thread principal parcourt les pages du mappage Ă  la recherche des octets des pages successives. Comme il n’y a pas eu encore d’accĂšs aux pages, le premier accĂšs Ă  un octet de chaque page dĂ©clenchera un Ă©vĂ©nement d’erreur de page sur le descripteur de fichier userfaultfd.

Chaque Ă©vĂ©nement d’erreur de page est gĂ©rĂ© par le second thread qui s’installe dans une boucle traitant l’entrĂ©e du descripteur de fichier userfaultfd. À chaque itĂ©ration de la boucle, le second thread appelle poll (2) pour vĂ©rifier l’état du descripteur de fichier puis lit un Ă©vĂ©nement Ă  partir de ce descripteur de fichier. Tout ce type d’évĂ©nements doit ĂȘtre un Ă©vĂ©nement UFFD_EVENT_PAGEFAULT que le thread traite en copiant un page de donnĂ©es dans la rĂ©gion en erreur en utilisant l’opĂ©ration d’ ioctl (2) UFFDIO_COPY .

La suite est un exemple de ce qui est observĂ© lors de l’exĂ©cution du programme :

$ ./userfaultfd_demo 3
Address returned by mmap() = 0x7fd30106c000
fault_handler_thread():
poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106c00f
(uffdio_copy.copy returned 4096)
Read address 0x7fd30106c00f in main(): A
Read address 0x7fd30106c40f in main(): A
Read address 0x7fd30106c80f in main(): A
Read address 0x7fd30106cc0f in main(): A
fault_handler_thread():
poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106d00f
(uffdio_copy.copy returned 4096)
Read address 0x7fd30106d00f in main(): B
Read address 0x7fd30106d40f in main(): B
Read address 0x7fd30106d80f in main(): B
Read address 0x7fd30106dc0f in main(): B
fault_handler_thread():
poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106e00f
(uffdio_copy.copy returned 4096)
Read address 0x7fd30106e00f in main(): C
Read address 0x7fd30106e40f in main(): C
Read address 0x7fd30106e80f in main(): C
Read address 0x7fd30106ec0f in main(): C

Source du programme

/* userfaultfd_demo.c
Licensed under the GNU General Public License version 2 or later.
*/
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
static int page_size;
static void *
fault_handler_thread(void *arg)
{
int nready;
long uffd; /* descripteur du fichier userfaultfd */
ssize_t nread;
struct pollfd pollfd;
struct uffdio_copy uffdio_copy;
static int fault_cnt = 0; /* Nombres d’erreurs dĂ©jĂ  gĂ©rĂ©es */
static char *page = NULL;
static struct uffd_msg msg; /* Données lues à partir de userfaultfd */
uffd = (long) arg;
/* Créer une page qui sera copiée dans la région en erreur. */
if (page == NULL) {
page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
}
/* Boucle gérant les événements entrants sur le descripteur
de fichier userfaultfd. */
for (;;) {
/* Voir ce que poll() nous dit sur l’userfaultfd. */
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready == -1)
err(EXIT_FAILURE, "poll");
printf("\nfault_handler_thread():\n");
printf(" poll() returns: nready = %d; "
"POLLIN = %d; POLLERR = %d\n", nready,
(pollfd.revents & POLLIN) != 0,
(pollfd.revents & POLLERR) != 0);
/* Lire un Ă©vĂ©nement Ă  partir de l’userfaultfd. */
nread = read(uffd, &msg, sizeof(msg));
if (nread == 0) {
printf("EOF on userfaultfd!\n");
exit(EXIT_FAILURE);
}
if (nread == -1)
err(EXIT_FAILURE, "read");
/* Un seul type d’évĂ©nement est attendu ; il faut vĂ©rifier
cette supposition. */
if (msg.event != UFFD_EVENT_PAGEFAULT) {
fprintf(stderr, "Unexpected event on userfaultfd\n");
exit(EXIT_FAILURE);
}
/* Afficher une information sur l’évĂ©nement erreur de page. */
printf(" UFFD_EVENT_PAGEFAULT event: ");
printf("flags = %"PRIx64"; ", msg.arg.pagefault.flags);
printf("address = %"PRIx64"\n", msg.arg.pagefault.address);
/* Copier la page sur laquelle pointe la « page » dans la région
fautive. Varier le contenu copiĂ©, afin qu’il soit plus
évident que chaque erreur soit gérée séparément. */
memset(page, 'A' + fault_cnt % 20, page_size);
fault_cnt++;
uffdio_copy.src = (unsigned long) page;
/* Il est nécessaire de gérer les erreurs de page en
unités de pages(!). Aussi, il faut arrondir les
adresses fautives Ă  la limite de page. */
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address &
~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
err(EXIT_FAILURE, "ioctl-UFFDIO_COPY");
printf(" (uffdio_copy.copy returned %"PRId64")\n",
uffdio_copy.copy);
}
}
int
main(int argc, char *argv[])
{
int s;
char c;
char *addr; /* Début de la région gérée par userfaultfd */
long uffd; /* Descripteur de fichier userfaultfd */
size_t len, l; /* Taille de la région gérée par userfaultfd */
pthread_t thr; /* ID du thread qui gĂšre les erreurs de page */
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
if (argc != 2) {
fprintf(stderr, "Utilisation : %s num-pages\n", argv[0]);
exit(EXIT_FAILURE);
}
page_size = sysconf(_SC_PAGE_SIZE);
len = strtoull(argv[1], NULL, 0) * page_size;
/* Créer et activer un objet userfaultfd. */
uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
err(EXIT_FAILURE, "userfaultfd");
/* NOTE : Une connexion de fonction en deux étapes est inutile ici,
dans la mesure oĂč l’exemple n’a besoin d’aucune fonction
particuliĂšre.
Les programmes qui *agissent* doivent appeler UFFDIO_API deux fois :
une fois avec « features = 0 » pour détecter les fonctions prises en
charge par ce noyau, puis avec le sous-ensemble de fonctions que le
programme veut vraiment activer. */
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
err(EXIT_FAILURE, "ioctl-UFFDIO_API");
/* Créer un mappage anonyme privé. La mémoire sera paginée
avec aucune demande — c’est-Ă -dire, sans ĂȘtre encore
allouée. Quand la mémoire sera réellement utilisée,
elle sera allouĂ©e au moyen de l’userfaultfd. */
addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
printf("Address returned by mmap() = %p\n", addr);
/* Enregistrer l’intervalle de mĂ©moire du mappage qui vient d’ĂȘtre
créé pour le traitement par l’objet userfaultfd. Dans mode,
suivre les pages manquantes (c’est-à-dire, les pages qui ne sont
pas encore fautives). */
uffdio_register.range.start = (unsigned long) addr;
uffdio_register.range.len = len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
err(EXIT_FAILURE, "ioctl-UFFDIO_REGISTER");
/* Créer un thread qui traitera les événements userfaultfd. */
s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd);
if (s != 0) {
errc(EXIT_FAILURE, s, "pthread_create");
}
/* Le thread principal utilise la mémoire dans le mappage,
utilisant des emplacements séparés de 1024 octets. Cela va
déclencher des événements userfaultfd pour toutes les pages
dans la région. */
l = 0xf; /* Assurer que l’adresse fautive n’est pas sur une
limite de page afin de vérifier que ce cas est
correctement géré dans le fault_handling_thread(). */
while (l < len) {
c = addr[l];
printf("Read address %p in %s(): ", addr + l, __func__);
printf("%c\n", c);
l += 1024;
usleep(100000); /* Ralentir un peu le traitement */
}
exit(EXIT_SUCCESS);
}

VOIR AUSSI

fcntl (2), ioctl (2), ioctl_userfaultfd (2), madvise (2), mmap (2)

Documentation/admin-guide/mm/userfaultfd.rst dans l’arborescence des sources du noyau Linux

TRADUCTION

La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n’y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org .