Provided by: manpages-fr-dev_4.23.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⟩.