plucky (2) userfaultfd.2.gz

Provided by: manpages-fr-dev_4.25.1-1_all bug

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_WRITEPROTECTuffdio_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   ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩   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⟩.