userfaultfd(2)

NAVN
userfaultfd - opret en fildeskriptor til at håndtere sidefejl i brugerrum

BIBLIOTEK
C-standardbibliotek (libc, -lc)

SYNOPSIS
#include <fcntl.h>             /* Definition af O_*-konstanter */
#include <sys/syscall.h>       /* Definition af SYS_*-konstanter */
#include <linux/userfaultfd.h> /* Definition af UFFD_*-konstanter */
#include <unistd.h>

int syscall(SYS_userfaultfd, int flag);

Note: glibc tilbyder intet omslag for userfaultfd(), så det er nødvendigt at bruge syscall(2).

BESKRIVELSE
userfaultfd() opretter et nyt userfaultfd-objekt, der kan bruges til uddelegering af side-fejl håndtering til et brugerrumsprogram og returnerer en fildeskriptor, der referer til det nye objekt. Det nye userfaultfd-objekt er konfigureret via ioctl(2).

Når først userfaultfd-objektet er konfigureret, så kan programmet bruge read(2) til at modtage userfauldtfd-påmindelser. Læsningerne fra userfaultfd kan være blokernede eller ikkeblokerende, afhængig af værdien af flag brugt til oprettelsen af userfaultfd eller efterfølgende kald til fcntl(2).

De følgende værdier kan være bitvis ORed i flag for at ændre opførelsen for userfaultfd():

O_CLOEXEC
    Aktiver flaget close-on-exec for den nye userfaultfd-fildeskiptor. Se beskrivelsen af flaget O_CLOEXEC i open(2).

O_NONBLOCK
    Aktiverer ikkeblokerende operation for userfaultfd-objektet. Se beskrivelsen af flaget O_NONBLOCK i open(2).

UFFD_USER_MODE_ONLY
    Dette er et userfaultfd-specifikt flag, der blev introduceret i Linux 5.11. Når angivet vil userfaultfd-objektet kun kunne håndtere sidefejl med oprindelse i brugerrummet på de registrerede regioner. Når en kerne-oprindelsesfejl bliver udløst på det registerede interval med denne userfaultdfd, så vil et SIGBUS-signal blive leveret.

Når den sidste fildeskriptor refererende til et userfaultfd-objekt er lukket, bliver alle hukommelsesintervallerne, der var registreret med objekter, afregistreret og ulæste hændelser tømmes.

Userfaultfd understøtter tre tilstande for registrering:

UFFDIO_REGISTER_MODE_MISSING (siden Linux 4.10)
    Når registreret med tilstanden UFFDIO_REGISTER_MODE_MISSING vil brugerrummet modtage en side-fejl påmindelse, når en manglende side tilgås. Den fejlede tråd vil blive stoppet fra afvikling indtil sidefejlen er løst fra brugerrum af enten en UFFDIO_COPY eller en UFFDIO_ZEROPAGE ioctl.

UFFDIO_REGISTER_MODE_MINOR (siden Linux 5.13)
    Når registreret med tilstanden UFFDIO_REGISTER_MODE_MINOR vil brugerrummet modtage en side-fejl påmindelse, når en mindre sidefejl opstår. Det vil sige når bagsiden er i sidens cache, men sidetabelelementerne endnu ikke findes. Den fejlede tråd vil blive stoppet fra afvikling indtil sidefejlen er løst fra brugerrum af UFFDIO_CONTINUE ioctl.

UFFDIO_REGISTER_MODE_WP (siden Linux 5.7)
    Når registreret med tilstanden UFFDIO_REGISTER_MODE_WP vil brugerrummet modtage en side-fejl påmindelse, når en skrivebeskyttet side skrives. Den fejlede tråd vil blive stoppet fra afvikling indtil brugerrummet fjerne skrivebeskyttelsen for siden via en UFFDIO_WRITEPROTECT ioctl.

Flere tilstande kan aktiveres på samme tid for det samme hukommelsesinterval.

Siden Linux 4.14 kan en userfaultfd-sidefejlpåmindelse selektivt indlejre fejlende tråd-id-information i påmindelsen. Man skal eksplicit aktivere denne funktion via UFFD_FEATURE_THREAD_ID-bit når userfaultfd-konteksten initialiseres. Som standard er tråd-id-rapportering deaktiveret.

  Brug
Userfaultfd-mekanismen er designet til at tillade at en tråd i et flertrådet program kan udføre en brugerrums søgning for andre tråde i processen. Når en sidefejl opstår for en af regionerne registreret med userfaultfd-objektet, så lægges den fejlende tråd i søvn og en hændelse oprettes, der kan læses via userfaultfd-fildeskriptoren. Den fejlhåndterende tråd læser hændelser fra denne fildeskriptor og betjener dem via operationerne beskrivet i ioctl_userfaultfd(2). Under betjening af sidefejlhændelser kan den fejlhåndterende tråd udløse en vækning for den sovende tråd.

Det er muligt for de fejlende tråde og de fejlhåndterende tråde at køre i konteksten af forskellige processer. I dette tilfælde, kan disse tråde tilhøre forskellige programmer, og programmet der afvikler de fejlende tråde vil ikke nødvendigvis samarbejde med programmet, der håndterer sidefejlene. I sådan ikkesamarbejdende tilstand skal processen der overvåger userfaultfd og håndterer sidefjel være opmærksom på ændringer i det virtuelle hukommelseslayout for den fejlende proces for at undgå hukommelseskorruption.

Siden Linux 4.11 kan userfaultfd også påminde de fejlhåndterende tråde om ændringer i det virtuelle hukommelseslayout for den fejlende proces. Derudover hvis den fejlende proces igangsætter fork(2), kan userfaultfd-objekterne associeret med overprocessen måske blive duplikeret i underprocessen og userfaultfd-overvågeren vil blive påmindet (via UFFD_EVENT_FORK beskrevet nedenfor) om fildeskriptoren associeret med userfault-objekterne opretter for underprocessen, der gør det muligt for userfaultfd-overvågeren at udføre brugerrumssøgning for underprocessen. Til forskel fra sidefejl der skal være synkrone og kræver en eksplicit eller implicit opvækning så leveres alle andre objekter asynkront og den ikkesamarbejdende proces genoptager afvikling så snart userfaultfd-håndteringen afvikler read(2). Userfaultfd-håndteringen bør omhyggeligt synkronisere kald til UFFDIO_COPY med behandlingen af hændelser.

Den nuværende asynkrone model for hændelseslevering er optimal for enkelttrådede ikkesamarbejdende userfaultfd-håndteringsimplementeringer.

Siden Linux 5.7 kan userfaultfd udføre synkron synkron sporing af beskidte sider via den nye skrivebeskyttede registertilstand. Man bør tjekke mod funktionsbit'en UFFD_FEATURE_PAGEFAULT_FLAG_WP før denne funktion anvendes. Svarende til den oprindelige userfaultfd missing-tilstand vil den skrivebeskyttede tilstand oprette en userfaultfd-påmindelse, når den beskyttede side skrives. Brugeren skal løse sidefejlen ved at fjerne beskyttelsen for den fejlende side og sparke den fejlede tråd i gang igen. For yderligere information kan du se i afsnittet »Userfaultfd write-protect-tilstand«.

  Userfaultfd-operation
Efter userfaultfd-objektet er oprettet med userfaultfd(), skal programmet aktivere det via operationen UFFDIO_API ioctl(2). Denne operation tillader et totrins handskake (håndtryk) mellem kernen og brugerummet for at bestemme hvilken API-verison og funktioner kernen understøtte, så aktivere de funktioner brugerummet ønsker. Denne operation skal udføres før nogle af ioctl(2)-operationerne beskrevet nedenfor (eller disse operationer fejler med fejlen EINVAL).

Efter en succesfuld UFFDIO_API-operation registrerer programmet hukommelsesadresseintervallerne via UFFDIO_REGISTER ioctl(2)-operationen. Efter en succesfuld fuldførelse af en UFFDIO_REGISTER-operation opstår der en sidefejl i det anmodte hukommelsesinterval og tilfredsstillende tilstanden defineret på registreringstidspunktet, vil blive omdirigeret af kernen til brugerrumsprogrammet. Programmet kan så bruge diverse (f.eks. UFFDIO_COPY, UFFDIO_ZEROPAGE eller UFFDIO_CONTINUE) ioctl(2)-operationer til at løse sidefejlen.

Siden Linux 4.14, hvis programmet angiver funktionsbit'en UFFD_FEATURE_SIGBUS ved at bruge UFFDIO_API ioctl(2), så vil ingen side-fejl påmindelse blive omdirigeret til brugerrum. I stedet leveres et SIGBUS-signal til den fejlende proces. Med denne funktion kan userfaultfd bruges til robusthedsformål til at fange enhver adgang til et område i det registrerede adresseinterval, der ikke har sider allokeret, uden at skulle lytte til userfaultfd-hændelser. Ingen userfaultfd-overvågning vil være krævet for at håndtere sådanne hukommelsesadgange. For eksempel kan denne funktion være nyttig for programmer, der ønsker at forhindre kernen i automatisk at allokere sider og udfylde huller i ikkesammenhængende filer, når hullet tilgåes via en hukommelsesoversættelse.

Funktionen UFFD_FEATURE_SIGBUS arves implicit via fork(2) hvis brugt sammen med UFFD_FEATURE_FORK.

Detaljer for de forskellige ioctl(2)-operationer kan findes i ioctl_userfaultfd(2).

Siden Linux 4.11, kan hændelser udover side-fejl aktiveret under UFFDIO_API-operationen.

Op til Linux 4.11 kan userfaultfd kun bruges med anonyme private hukommelsesoversættelser. Siden Linux 4.11 kan userfaultfd også bruges med hugetlbfs og delte hukommelsesoversættelser.

  Userfaultfd write-protect-tilstand (siden Linux 5.7)
Siden Linux 5.7 understøtter userfaultfd skrivebeskyttet tilstand for anonym hukommelse. Brugeren skal først tjekke tilgængelighed for denne funktion via UFFDIO_API ioctl mod funktionsbit'en UFFD_FEATURE_PAGEFAULT_FLAG_WP før denne funkton bruges.

Siden Linux 5.19 var den skrivebeskyttede tilstand også understøttet på shmem- og hugetlbfs-hukommelsestyper. Den kan registreres med funktionsbit'en UFFD_FEATURE_WP_HUGETLBFS_SHMEM.

For at registrere med userfaultfd i skrivebeskyttet tilstand skal brugeren initialisere UFFDIO_REGISTER ioctl med tilstanden UFFDIO_REGISTER_MODE_WP angivet. Bemærk at det er legalt at overvåge det samme hukommelsesinterval med flere tilstande. For eksempel kan brugeren bruge UFFDIO_REGISTER med tilstanden angivet til UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP. Når der kun er UFFDIO_REGISTER_MODE_WP registreret vil brugerrummet ikke modtage nogen påmindelse når en manglende side skrives. I stedet vil brugerrummet kun modtage en skrivebeskyttet side-fejl påmindelse, når en eksisterende men skrivebeskyttet side blev skrevet.

Efter UFFDIO_REGISTER ioctl fuldførtes med UFFDIO_REGISTER_MODE_WP-tilstanden angivet, kan brugeren skrivebeskytte enhver eksisterende hukommelse i intervallet, der bruger ioctl UFFDIO_WRITEPROTECT hvor uffdio_writeprotect.mode bør være angivet til UFFDIO_WRITEPROTECT_MODE_WP.

Når en skrivebeskyttet hændelse opstår, så vil brugerrummet modtage en side-fejl påmindelse hvis uffd_msg.pagefault.flags vil være med UFFD_PAGEFAULT_FLAG_WP-flaget angivet. Bemærk: da kun skrivninger kan udløse denne slags fjel, vil skrivebeskyttede påmindelser altid have UFFD_PAGEFAULT_FLAG_WRITE-bit'en angivet sammen med UFFD_PAGEFAULT_FLAG_WP-bit'en.

For at løse en skrivebeskyttet sidefejl bør brugeren initiere endnu en UFFDIO_WRITEPROTECT ioctl, hvis uffd_msg.pagefault.flags skal have flaget UFFDIO_WRITEPROTECT_MODE_WP ryddet på den fejlende side eller interval.

  Userfaultfd minor fault-tilstand (siden Linux 5.13)
Siden Linux 5.13 understøtter userfaultfd minor fault-tilstand. I denne tilstand fremstilles fejlbeskeder ikke for væsentlige fejl (hvor siden manglede), men derimod mindre fejl, hvor en side findes i sidemellemlageret, men sidetabelelementerne er endnu ikke til stede. Brugeren skal først tjekke tilgængelighed for denne funktion via UFFDIO_API ioctl med det passende funktionsbitsæt før denne funktion bruges: UFFD_FEATURE_MINOR_HUGETLBFS siden Linux 5.13, eller UFFD_FEATURE_MINOR_SHMEM siden Linux 5.14.

For at registrere med userfaultfd minor fault-tilstanden skal brugeren igangsætte UFFDIO_REGISTER ioctl med tilstanden UFFD_REGISTER_MODE_MINOR angivet.

Når der opstår en mindre fejl vil brugerrummet modtage en side-fejl-påmindelse hvis uffd_msg.pagefault.flags vil have flaget UFFD_PAGEFAULT_FLAG_MINOR angivet.

For at løse en mindre sidefejl bør håndteringen beslutte hvorvidt det eksisterende sideindhold først skal ændres. Hvis det skal, så skal det gøres via en anden, ikke-userfaultfd-registreret oversættelse til den samme bagside (f.eks. ved at oversætte shmem- eller hugetlbfs-filen to gange). Når først siden ses som »opdateret« kan fejlen løses ved at initiere UFFDIO_CONTINUE ioctl, der installerer sidetabelelementerne og (som standard) vækker de fejlende tråde.

Minor fault-tilstanden understøtter kun hugetlbfs-understøttet (siden Linux 5.13) og shmem-understøttet (siden Linux 5.14) hukommelse.

  Læsning fra userfaultfd-strukturen
Hver read(2) fra userfaultfd-fildeskriptoren returnerer en eller flere uffd_msg-strukturer, der hver beskriver en side-fejl hændelse eller en hændelse krævet for den ikke-samarbejdende userfaultfd-brug:

    struct uffd_msg {
        __u8  event;            /* Hændelsestype */
        ...
        union {
            struct {
                __u64 flags;    /* Flag der beskriver fejl */
                __u64 address;  /* Fejlende adresse */
                union {
                    __u32 ptid; /* Tråd-id for fejlen */
                } feat;
            } pagefault;

            struct {            /* Siden Linux 4.11 */
                __u32 ufd;      /* Userfault-fildeskriptor
                                   for underprocessen */
            } fork;

            struct {            /* Siden Linux 4.11 */
                __u64 from;     /* Gammel adresse for omfordelt område */
                __u64 to;       /* Ny adresse for omfordelt område */
                __u64 len;      /* Oprindelig oversættelsesstørrelse */
            } remap;

            struct {            /* Siden Linux 4.11 */
                __u64 start;    /* Startadresse for fjernet område */
                __u64 end;      /* Slutadresse for fjernet område */
            } remove;
            ...
        } arg;

        /* Mellemrumsfelter udeladt */
    } __packed;

Hvis flere hændelser er tilgængelige og det angivne mellemlager er stort nok, så returnerer read(2) så mange hændelser som passer ind i det angivne mellemlager. Hvis mellemlageret angivet for read(2) er mindre end størrelsen på uffd_msg-strukturen, så fejler read(2) med fejlen EINVAL.

Felterne angivet i strukturen uffd_msg er som følgende:

event
    Hændelsestypen. Afhængig af hændelsestypen repræsenterer forskellige felter for arg-foreningen detaljer krævet for hændelsesbehandlingen. Hændelser for ikke-page-fault oprettes kun når passende funktion er aktiveret under API-håndtrykket med UFFDIO_API ioctl(2).

    De følgende værdier kan fremgå af feltet event:

    UFFD_EVENT_PAGEFAULT (siden Linux 4.3)
        En sidefejl-hændelse. Sidefejl-detaljer er tilgængelige i feltet pagefault.

    UFFD_EVENT_FORK (siden Linux 4.11)
        Oprettet når den fejlende proces igangsætter fork(2) (eller clone(2) uden flaget CLONE_VM). Hændelsesdetaljerne er tilgængelige i feltet fork.

    UFFD_EVENT_REMAP (siden Linux 4.11)
        Oprettet når den fejlende proces igangsætter mremap(2). Hændelsesdetaljerne er tilgængelig i feltet remap.

    UFFD_EVENT_REMOVE (siden Linux 4.11)
        Oprettet når den fejlende proces igangsætter madvise(2) med rådet MADV_DONTNEED eller MADV_REMOVE. Hændelsesdetaljerne er tilgængelige i feltet remove.

    UFFD_EVENT_UNMAP (siden Linux 4.11)
        Oprettet når den fejlende proces fjerner kortlægning af et hukommelsesinterval, enten eksplicit via munmap(2) eller implicit under mmap(2) eller mremap(2). Hændelsesdetaljerne er tilgængelige i feltet remove.

pagefault.address
    Adressen der udløste sidefejlen.

pagefault.flags
    En bit-maske af flag, der beskriver hændelsen. For UFFD_EVENT_PAGEFAULT, kan det følgende flag måske fremgå:

    UFFD_PAGEFAULT_FLAG_WP
        Hvis dette flag er angivet, så var fejlen en skrivebeskyttelse.

    UFFD_PAGEFAULT_FLAG_MINOR
        Hvis dette flag er angivet, så var fejlen en mindre fejl.

    UFFD_PAGEFAULT_FLAG_WRITE
        Hvis dette flag er angivet, så var fejlen en skrivefejl.

    Hvis hverken UFFD_PAGEFAULT_FLAG_WP eller UFFD_PAGEFAULT_FLAG_MINOR er angivet, så var fejlen en manglende fejl.

pagefault.feat.pid
    Tråd-id'et der udløste sidefejlen.

fork.ufd
    Fildeskriptoren associeret med brugerfejlobjektet oprettet for underprocesen oprettet af fork(2).

remap.from
    Den oprindelige adresse for hukommelsesintervallet, der blev omarrangeret via mremap(2).

remap.to
    Den nye adresse for hukommelsesintervallet, der blev omarrangeret via mremap(2).

remap.len
    Den oprindelige størrelse på hukommelsesintervallet, der blev omarrangeret via mremap(2).

remove.start
    Startadressen for hukommelsesintervallet, der blev frigjort via madvise(2) eller ej arrangeret

remove.end
    Slutadressen for hukommelsesintervallet, der blev frigjort via madvise(2) eller ej arrangeret

Som read(2) på en userfaultfd kan en fildeskriptor fejle med de følgende fejl:

EINVAL
    Userfaultfd-objektet er endnu ikke blevet aktiveret via UFFDIO_API ioctl(2)-operationen

Hvis flaget O_NONBLOCK er aktiveret i den associerede åbne filbeskrivelse, kan userfaultfd-fildeskriptoren overvåges med poll(2), select(2) og epoll(7). Når hændelser er tilgængelige indikerer fildeskriptoren at den kan læses. Hvis flaget O_NONBLOCK ikke er aktiveret, så indikerer poll(2) (altid) at filen har en POLLERR-betingelse og select(2) indikerer fildeskriptoren som både læs- og skrivbar.

RETURVÆRDI
Ved succes returnerer userfaultfd() en ny fildeskriptor, der refererer til userfaultfd-objektet. Ved fejl returneres -1 og errno angives for at indikere fejlen.

FEJL

EINVAL
    En ej understøttet værdi var angivet i flag.

EMFILE
    Per proces-begrænsningen på antallet af åbne fildeskriptorer, der er nået

ENFILE
    Systemets begrænsning på det samlede antal åbne filer er nået.

ENOMEM
    Utilstrækkelig kernehukommelse var tilgængelig.

EPERM (siden Linux 5.2)
    Kalderen er ikke privilegeret (har ikke en CAP_SYS_PTRACE-kapacitet i det oprindelige brugernavnerum), og /proc/sys/vm/unprivileged_userfaultfd har værdien 0.

STANDARDER
Linux.

HISTORIK
Linux 4.3.

Understøttelse for hugetlbfs og delte hukommelsesområder og ej-side-fejl-hændelser blev tilføjet i Linux 4.11

NOTER
Userfaultfd-mekanismen kan bruges som et alternativ til de traditonelle brugerrumssideteknikker baseret på brugen af signalet SIGSEGV og mmap(2). Kan også bruges til at implementere lazy restore for checkpoint/restore-mekanismer, samt post-copy-migrering for at tillade (næsten) uforstyrret afvikling under overførsel af virtuelle maskiner og Linux-containere fra en vært til en anden.

FEJL
Hvis UFFD_FEATURE_EVENT_FORK er aktiveret og et systemkald fra fork(2)-familien afbrydes af et signal eller fejl kan en gammel userfaultfd-deskriptor blive oprettet. I dette tilfælde vil en falsk UFFD_EVENT_FORK blive leveret til userfaultfd-overvågeren.

EKSEMPLER
Programmet nedenfor demonstrerer brugen af userfaultfd-mekanismen. Programmet opretter to tråde, en fungerer som side-fejl-håndteringen for processen, for siderne i en demand-page zero-region oprettet via mmap(2).

Programmet bruger et kommandolinjeargument, hvilket er antallet af sider, der vil blive oprettet i en oversættelse, hvis sidefejl vil blive håndteret via userfaultfd. Efter oprettelse af et userfaultfd-objekt opretter programmet en anoynm privat oversættelse af den angivne størrelse og registerer adresseintervallet for den oversættelse via operationen UFFDIO_REGISTER ioctl(2). Programmet opretter så endnu en tråd, der vil udføre opgaven med at håndtere sidefejl.

Hovedtråden løber så igennem siderne for oversættelsen ved at hente byte fra successive sider. Da siderne endnu ikke er blevet tilgået vil den første adgang af en byte i hver side udløse en side-fejl-hændelse på userfaultfd-fildeskriptoren.

Hver af side-fejl-hændelserne håndteres af den anden tråd, der befinder sig i en løkke, der behandler data fra userfaultfd-fildeskriptoren. I hver løkke-gennemgang, kalder den anden tråd først poll(2) for at tjekke tilstanden for fildeskriptoren, og læser så en hændelse fra fildeskriptoren. Alle sådanne hændelser bør være UFFD_EVENT_PAGEFAULT-hændelser, som tråden håndterer ved at kopiere en side med data ind i den fejlende region via operationen UFFDIO_COPY ioctl(2).

Det følgende er et eksempel på hvad vi ser, når programmet afvikles:

    $ ./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

  Programkilde
    /* userfaultfd_demo.c

       Udgivet under GNU General Public License version 2 eller senere.
    */
    #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)
    {
        long  uffd;   /* userfaultfd-fildeskriptor */

        static int      fault_cnt = 0; /* Fejlantal håndteret indtil videre */
        static char     *page = NULL;
        static struct uffd_msg  msg;  /* Data læst fra userfaultfd */

        uffd = (long) arg;

        /* Opret en side der vil blive kopieret ind i den fejlende region.  */

        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");
        }

        /* Løkke, der håndterer indgående hændelser på userfaultfd-
           fildeskriptoren.  */

        for (;;) {
            int                 nready;
            ssize_t             nread;
            struct pollfd       pollfd;
            struct uffdio_copy  uffdio_copy;

            /* Se hvad poll() fortæller os om 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);

            /* Læs en hændelse fra 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");

            /* Vi forventer kun en slags hændelse; verificer den antagelse.  */

            if (msg.event != UFFD_EVENT_PAGEFAULT) {
                fprintf(stderr, "Unexpected event on userfaultfd\n");
                exit(EXIT_FAILURE);
            }

            /* Vis info om hændelsen side-fejl.  */

            printf("    UFFD_EVENT_PAGEFAULT event: ");
            printf("flags = %w64x; ", msg.arg.pagefault.flags);
            printf("address = %w64x\n", msg.arg.pagefault.address);

            /* Kopier siden peget mod af 'page' ind i den fejlende
               region. Varier indholdet der kopieres ind, så det er mere
               indlysende at hver fejl håndteres separat.  */

            memset(page, 'A' + fault_cnt % 20, page_size);
            fault_cnt++;

            uffdio_copy.src = (unsigned long) page;

            /* Vi skal håndtere sidefejl i sideenheder(!).
               Så afrund fejlende adresse ned til sidegrænsen.  */

            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 %w64d)\n",
                   uffdio_copy.copy);
        }
    }

    int
    main(int argc, char *argv[])
    {
        int        s;
        char       *addr;   /* Start på region håndteret af userfaultfd */
        long       uffd;    /* userfaultfd-fildeskriptor */
        size_t     size, i;  /* Regionstørrelse håndteret af userfaultfd */
        pthread_t  thr;     /* Id for tråd der håndterer sidefejl */
        struct uffdio_api       uffdio_api;
        struct uffdio_register  uffdio_register;

        if (argc != 2) {
            fprintf(stderr, "Usage: %s num-pages\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        page_size = sysconf(_SC_PAGE_SIZE);
        size = strtoull(argv[1], NULL, 0) * page_size;

        /* Opret og aktiver userfaultfd-objekt.  */

        uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK);
        if (uffd == -1)
            err(EXIT_FAILURE, "userfaultfd");

        /* BEMÆRK: To-trins funktionen handshake (håndtryk) er ikke krævet her
           da dette eksempel ikke kræver nogle specifikke funktioner.

           Programmer der *skal* dette bør kalde UFFDIO_API to gange: en gang
           med »features = 0« for at registrere funktioner understøttet af denne
           kerne og igen med undersættet af funktionerne programmet faktisk
           ønsker at aktivere.  */
        uffdio_api.api = UFFD_API;
        uffdio_api.features = 0;
        if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
            err(EXIT_FAILURE, "ioctl-UFFDIO_API");

        /* Opret en privat anonym oversættelse. Hukommelsen vil være
           demand-zero paged - det vil side enndu ikke allokeret. Når vi
           reelt rører hukommelsen, så vil den blive allokeret via
           userfaultfd.  */

        addr = mmap(NULL, size, 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);

        /* Registrer hukommelsesintervallet for oversættelsen vi lige oprettede
           til at blive håndteret af userfaultfd-objektet. I tilstand, anmoder
           vi om at registrere manglende sider (dvs. sider der endnu ikke er
           fejlet ind).  */

        uffdio_register.range.start = (unsigned long) addr;
        uffdio_register.range.len = size;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
        if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
            err(EXIT_FAILURE, "ioctl-UFFDIO_REGISTER");

        /* Opret en tråd der vil behandle userfaultfd-hændelser.  */

        s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd);
        if (s != 0) {
            errc(EXIT_FAILURE, s, "pthread_create");
        }

        /* Hovedtråden rører nu hukommelse i oversættelsen, placeringer røres
           1024 byte fra hinanden. Dette vil udløse userfaultfd-hændelser
           for alle sider i regionen.  */

        i = 0xf;    /* Sikr at den fejlende adresse ikke er på en sidegrænse,
                       for at teste, at vi håndterede tilfældet korrekt
                       i fault_handling_thread().  */
        while (i < size) {
            char  c;

            c = addr[i];
            printf("Read address %p in %s(): ", addr + i, __func__);
            printf("%c\n", c);
            i += 1024;
            usleep(100000);         /* Gør tingene lidt langsommere */
        }

        exit(EXIT_SUCCESS);
    }

SE OGSÅ
fcntl(2), ioctl(2), ioctl_userfaultfd(2), madvise(2), mmap(2)

Documentation/admin-guide/mm/userfaultfd.rst i Linux-kernekildetræet

OVERSÆTTELSE
Oversættere af denne manual til dansk Joe Hansen <joedalton2@yahoo.dk>

Denne oversættelse er gratis dokumentation; læs GNU General Public License version 3 (https://www.gnu.org/licenses/gpl-3.0.html) eller nyere for ophavsretbetingelser. Der er INGEN ANSVAR.

Hvis du støder på fejl i oversættelsen af ​​denne vejledning, skal du sende en besked til debian-l10n-danish@lists.debian.org.