userfaultfd(2) System Calls Manual userfaultfd(2) NAVN userfaultfd - opret en fildeskriptor til at handtere sidefejl i brugerrum BIBLIOTEK C-standardbibliotek (libc, -lc) SYNOPSIS #include /* Definition af O_*-konstanter */ #include /* Definition af SYS_*-konstanter */ #include /* Definition af UFFD_*-konstanter */ #include int syscall(SYS_userfaultfd, int flag); Note: glibc tilbyder intet omslag for userfaultfd(), sa det er nodvendigt at bruge syscall(2). BESKRIVELSE userfaultfd() opretter et nyt userfaultfd-objekt, der kan bruges til uddelegering af side-fejl handtering til et brugerrumsprogram og returnerer en fildeskriptor, der referer til det nye objekt. Det nye userfaultfd-objekt er konfigureret via ioctl(2). Nar forst userfaultfd-objektet er konfigureret, sa kan programmet bruge read(2) til at modtage userfauldtfd-pamindelser. Laesningerne fra userfaultfd kan vaere blokernede eller ikkeblokerende, afhaengig af vaerdien af flag brugt til oprettelsen af userfaultfd eller efterfolgende kald til fcntl(2). De folgende vaerdier kan vaere bitvis ORed i flag for at aendre opforelsen 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. Nar angivet vil userfaultfd-objektet kun kunne handtere sidefejl med oprindelse i brugerrummet pa de registrerede regioner. Nar en kerne-oprindelsesfejl bliver udlost pa det registerede interval med denne userfaultdfd, sa vil et SIGBUS-signal blive leveret. Nar den sidste fildeskriptor refererende til et userfaultfd-objekt er lukket, bliver alle hukommelsesintervallerne, der var registreret med objekter, afregistreret og ulaeste haendelser tommes. Userfaultfd understotter tre tilstande for registrering: UFFDIO_REGISTER_MODE_MISSING (siden Linux 4.10) Nar registreret med tilstanden UFFDIO_REGISTER_MODE_MISSING vil brugerrummet modtage en side-fejl pamindelse, nar en manglende side tilgas. Den fejlede trad vil blive stoppet fra afvikling indtil sidefejlen er lost fra brugerrum af enten en UFFDIO_COPY eller en UFFDIO_ZEROPAGE ioctl. UFFDIO_REGISTER_MODE_MINOR (siden Linux 5.13) Nar registreret med tilstanden UFFDIO_REGISTER_MODE_MINOR vil brugerrummet modtage en side-fejl pamindelse, nar en mindre sidefejl opstar. Det vil sige nar bagsiden er i sidens cache, men sidetabelelementerne endnu ikke findes. Den fejlede trad vil blive stoppet fra afvikling indtil sidefejlen er lost fra brugerrum af UFFDIO_CONTINUE ioctl. UFFDIO_REGISTER_MODE_WP (siden Linux 5.7) Nar registreret med tilstanden UFFDIO_REGISTER_MODE_WP vil brugerrummet modtage en side-fejl pamindelse, nar en skrivebeskyttet side skrives. Den fejlede trad vil blive stoppet fra afvikling indtil brugerrummet fjerne skrivebeskyttelsen for siden via en UFFDIO_WRITEPROTECT ioctl. Flere tilstande kan aktiveres pa samme tid for det samme hukommelsesinterval. Siden Linux 4.14 kan en userfaultfd-sidefejlpamindelse selektivt indlejre fejlende trad-id-information i pamindelsen. Man skal eksplicit aktivere denne funktion via UFFD_FEATURE_THREAD_ID-bit nar userfaultfd-konteksten initialiseres. Som standard er trad-id-rapportering deaktiveret. Brug Userfaultfd-mekanismen er designet til at tillade at en trad i et flertradet program kan udfore en brugerrums sogning for andre trade i processen. Nar en sidefejl opstar for en af regionerne registreret med userfaultfd-objektet, sa laegges den fejlende trad i sovn og en haendelse oprettes, der kan laeses via userfaultfd-fildeskriptoren. Den fejlhandterende trad laeser haendelser fra denne fildeskriptor og betjener dem via operationerne beskrivet i ioctl_userfaultfd(2). Under betjening af sidefejlhaendelser kan den fejlhandterende trad udlose en vaekning for den sovende trad. Det er muligt for de fejlende trade og de fejlhandterende trade at kore i konteksten af forskellige processer. I dette tilfaelde, kan disse trade tilhore forskellige programmer, og programmet der afvikler de fejlende trade vil ikke nodvendigvis samarbejde med programmet, der handterer sidefejlene. I sadan ikkesamarbejdende tilstand skal processen der overvager userfaultfd og handterer sidefjel vaere opmaerksom pa aendringer i det virtuelle hukommelseslayout for den fejlende proces for at undga hukommelseskorruption. Siden Linux 4.11 kan userfaultfd ogsa paminde de fejlhandterende trade om aendringer i det virtuelle hukommelseslayout for den fejlende proces. Derudover hvis den fejlende proces igangsaetter fork(2), kan userfaultfd-objekterne associeret med overprocessen maske blive duplikeret i underprocessen og userfaultfd-overvageren vil blive pamindet (via UFFD_EVENT_FORK beskrevet nedenfor) om fildeskriptoren associeret med userfault-objekterne opretter for underprocessen, der gor det muligt for userfaultfd-overvageren at udfore brugerrumssogning for underprocessen. Til forskel fra sidefejl der skal vaere synkrone og kraever en eksplicit eller implicit opvaekning sa leveres alle andre objekter asynkront og den ikkesamarbejdende proces genoptager afvikling sa snart userfaultfd-handteringen afvikler read(2). Userfaultfd-handteringen bor omhyggeligt synkronisere kald til UFFDIO_COPY med behandlingen af haendelser. Den nuvaerende asynkrone model for haendelseslevering er optimal for enkelttradede ikkesamarbejdende userfaultfd-handteringsimplementeringer. Siden Linux 5.7 kan userfaultfd udfore synkron synkron sporing af beskidte sider via den nye skrivebeskyttede registertilstand. Man bor tjekke mod funktionsbit'en UFFD_FEATURE_PAGEFAULT_FLAG_WP for denne funktion anvendes. Svarende til den oprindelige userfaultfd missing-tilstand vil den skrivebeskyttede tilstand oprette en userfaultfd-pamindelse, nar den beskyttede side skrives. Brugeren skal lose sidefejlen ved at fjerne beskyttelsen for den fejlende side og sparke den fejlede trad 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 (handtryk) mellem kernen og brugerummet for at bestemme hvilken API-verison og funktioner kernen understotte, sa aktivere de funktioner brugerummet onsker. Denne operation skal udfores for 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 fuldforelse af en UFFDIO_REGISTER-operation opstar der en sidefejl i det anmodte hukommelsesinterval og tilfredsstillende tilstanden defineret pa registreringstidspunktet, vil blive omdirigeret af kernen til brugerrumsprogrammet. Programmet kan sa bruge diverse (f.eks. UFFDIO_COPY, UFFDIO_ZEROPAGE eller UFFDIO_CONTINUE) ioctl(2)-operationer til at lose sidefejlen. Siden Linux 4.14, hvis programmet angiver funktionsbit'en UFFD_FEATURE_SIGBUS ved at bruge UFFDIO_API ioctl(2), sa vil ingen side-fejl pamindelse blive omdirigeret til brugerrum. I stedet leveres et SIGBUS-signal til den fejlende proces. Med denne funktion kan userfaultfd bruges til robusthedsformal til at fange enhver adgang til et omrade i det registrerede adresseinterval, der ikke har sider allokeret, uden at skulle lytte til userfaultfd-haendelser. Ingen userfaultfd-overvagning vil vaere kraevet for at handtere sadanne hukommelsesadgange. For eksempel kan denne funktion vaere nyttig for programmer, der onsker at forhindre kernen i automatisk at allokere sider og udfylde huller i ikkesammenhaengende filer, nar hullet tilgaes via en hukommelsesoversaettelse. 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 haendelser udover side-fejl aktiveret under UFFDIO_API-operationen. Op til Linux 4.11 kan userfaultfd kun bruges med anonyme private hukommelsesoversaettelser. Siden Linux 4.11 kan userfaultfd ogsa bruges med hugetlbfs og delte hukommelsesoversaettelser. Userfaultfd write-protect-tilstand (siden Linux 5.7) Siden Linux 5.7 understotter userfaultfd skrivebeskyttet tilstand for anonym hukommelse. Brugeren skal forst tjekke tilgaengelighed for denne funktion via UFFDIO_API ioctl mod funktionsbit'en UFFD_FEATURE_PAGEFAULT_FLAG_WP for denne funkton bruges. Siden Linux 5.19 var den skrivebeskyttede tilstand ogsa understottet pa 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. Bemaerk at det er legalt at overvage 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. Nar der kun er UFFDIO_REGISTER_MODE_WP registreret vil brugerrummet ikke modtage nogen pamindelse nar en manglende side skrives. I stedet vil brugerrummet kun modtage en skrivebeskyttet side-fejl pamindelse, nar en eksisterende men skrivebeskyttet side blev skrevet. Efter UFFDIO_REGISTER ioctl fuldfortes med UFFDIO_REGISTER_MODE_WP-tilstanden angivet, kan brugeren skrivebeskytte enhver eksisterende hukommelse i intervallet, der bruger ioctl UFFDIO_WRITEPROTECT hvor uffdio_writeprotect.mode bor vaere angivet til UFFDIO_WRITEPROTECT_MODE_WP. Nar en skrivebeskyttet haendelse opstar, sa vil brugerrummet modtage en side-fejl pamindelse hvis uffd_msg.pagefault.flags vil vaere med UFFD_PAGEFAULT_FLAG_WP-flaget angivet. Bemaerk: da kun skrivninger kan udlose denne slags fjel, vil skrivebeskyttede pamindelser altid have UFFD_PAGEFAULT_FLAG_WRITE-bit'en angivet sammen med UFFD_PAGEFAULT_FLAG_WP-bit'en. For at lose en skrivebeskyttet sidefejl bor brugeren initiere endnu en UFFDIO_WRITEPROTECT ioctl, hvis uffd_msg.pagefault.flags skal have flaget UFFDIO_WRITEPROTECT_MODE_WP ryddet pa den fejlende side eller interval. Userfaultfd minor fault-tilstand (siden Linux 5.13) Siden Linux 5.13 understotter userfaultfd minor fault-tilstand. I denne tilstand fremstilles fejlbeskeder ikke for vaesentlige fejl (hvor siden manglede), men derimod mindre fejl, hvor en side findes i sidemellemlageret, men sidetabelelementerne er endnu ikke til stede. Brugeren skal forst tjekke tilgaengelighed for denne funktion via UFFDIO_API ioctl med det passende funktionsbitsaet for 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 igangsaette UFFDIO_REGISTER ioctl med tilstanden UFFD_REGISTER_MODE_MINOR angivet. Nar der opstar en mindre fejl vil brugerrummet modtage en side-fejl-pamindelse hvis uffd_msg.pagefault.flags vil have flaget UFFD_PAGEFAULT_FLAG_MINOR angivet. For at lose en mindre sidefejl bor handteringen beslutte hvorvidt det eksisterende sideindhold forst skal aendres. Hvis det skal, sa skal det gores via en anden, ikke-userfaultfd-registreret oversaettelse til den samme bagside (f.eks. ved at oversaette shmem- eller hugetlbfs-filen to gange). Nar forst siden ses som >>opdateret<< kan fejlen loses ved at initiere UFFDIO_CONTINUE ioctl, der installerer sidetabelelementerne og (som standard) vaekker de fejlende trade. Minor fault-tilstanden understotter kun hugetlbfs-understottet (siden Linux 5.13) og shmem-understottet (siden Linux 5.14) hukommelse. Laesning fra userfaultfd-strukturen Hver read(2) fra userfaultfd-fildeskriptoren returnerer en eller flere uffd_msg-strukturer, der hver beskriver en side-fejl haendelse eller en haendelse kraevet for den ikke-samarbejdende userfaultfd-brug: struct uffd_msg { __u8 event; /* Haendelsestype */ ... union { struct { __u64 flags; /* Flag der beskriver fejl */ __u64 address; /* Fejlende adresse */ union { __u32 ptid; /* Trad-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 omrade */ __u64 to; /* Ny adresse for omfordelt omrade */ __u64 len; /* Oprindelig oversaettelsesstorrelse */ } remap; struct { /* Siden Linux 4.11 */ __u64 start; /* Startadresse for fjernet omrade */ __u64 end; /* Slutadresse for fjernet omrade */ } remove; ... } arg; /* Mellemrumsfelter udeladt */ } __packed; Hvis flere haendelser er tilgaengelige og det angivne mellemlager er stort nok, sa returnerer read(2) sa mange haendelser som passer ind i det angivne mellemlager. Hvis mellemlageret angivet for read(2) er mindre end storrelsen pa uffd_msg-strukturen, sa fejler read(2) med fejlen EINVAL. Felterne angivet i strukturen uffd_msg er som folgende: event Haendelsestypen. Afhaengig af haendelsestypen repraesenterer forskellige felter for arg-foreningen detaljer kraevet for haendelsesbehandlingen. Haendelser for ikke-page-fault oprettes kun nar passende funktion er aktiveret under API-handtrykket med UFFDIO_API ioctl(2). De folgende vaerdier kan fremga af feltet event: UFFD_EVENT_PAGEFAULT (siden Linux 4.3) En sidefejl-haendelse. Sidefejl-detaljer er tilgaengelige i feltet pagefault. UFFD_EVENT_FORK (siden Linux 4.11) Oprettet nar den fejlende proces igangsaetter fork(2) (eller clone(2) uden flaget CLONE_VM). Haendelsesdetaljerne er tilgaengelige i feltet fork. UFFD_EVENT_REMAP (siden Linux 4.11) Oprettet nar den fejlende proces igangsaetter mremap(2). Haendelsesdetaljerne er tilgaengelig i feltet remap. UFFD_EVENT_REMOVE (siden Linux 4.11) Oprettet nar den fejlende proces igangsaetter madvise(2) med radet MADV_DONTNEED eller MADV_REMOVE. Haendelsesdetaljerne er tilgaengelige i feltet remove. UFFD_EVENT_UNMAP (siden Linux 4.11) Oprettet nar den fejlende proces fjerner kortlaegning af et hukommelsesinterval, enten eksplicit via munmap(2) eller implicit under mmap(2) eller mremap(2). Haendelsesdetaljerne er tilgaengelige i feltet remove. pagefault.address Adressen der udloste sidefejlen. pagefault.flags En bit-maske af flag, der beskriver haendelsen. For UFFD_EVENT_PAGEFAULT, kan det folgende flag maske fremga: UFFD_PAGEFAULT_FLAG_WP Hvis dette flag er angivet, sa var fejlen en skrivebeskyttelse. UFFD_PAGEFAULT_FLAG_MINOR Hvis dette flag er angivet, sa var fejlen en mindre fejl. UFFD_PAGEFAULT_FLAG_WRITE Hvis dette flag er angivet, sa var fejlen en skrivefejl. Hvis hverken UFFD_PAGEFAULT_FLAG_WP eller UFFD_PAGEFAULT_FLAG_MINOR er angivet, sa var fejlen en manglende fejl. pagefault.feat.pid Trad-id'et der udloste 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 storrelse pa 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) pa en userfaultfd kan en fildeskriptor fejle med de folgende fejl: EINVAL Userfaultfd-objektet er endnu ikke blevet aktiveret via UFFDIO_API ioctl(2)-operationen Hvis flaget O_NONBLOCK er aktiveret i den associerede abne filbeskrivelse, kan userfaultfd-fildeskriptoren overvages med poll(2), select(2) og epoll(7). Nar haendelser er tilgaengelige indikerer fildeskriptoren at den kan laeses. Hvis flaget O_NONBLOCK ikke er aktiveret, sa indikerer poll(2) (altid) at filen har en POLLERR-betingelse og select(2) indikerer fildeskriptoren som bade laes- og skrivbar. RETURVAERDI 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 understottet vaerdi var angivet i flag. EMFILE Per proces-begraensningen pa antallet af abne fildeskriptorer, der er naet ENFILE Systemets begraensning pa det samlede antal abne filer er naet. ENOMEM Utilstraekkelig kernehukommelse var tilgaengelig. 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 vaerdien 0. STANDARDER Linux. HISTORIK Linux 4.3. Understottelse for hugetlbfs og delte hukommelsesomrader og ej-side-fejl-haendelser blev tilfojet i Linux 4.11 NOTER Userfaultfd-mekanismen kan bruges som et alternativ til de traditonelle brugerrumssideteknikker baseret pa brugen af signalet SIGSEGV og mmap(2). Kan ogsa bruges til at implementere lazy restore for checkpoint/restore-mekanismer, samt post-copy-migrering for at tillade (naesten) uforstyrret afvikling under overforsel af virtuelle maskiner og Linux-containere fra en vaert 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 tilfaelde vil en falsk UFFD_EVENT_FORK blive leveret til userfaultfd-overvageren. EKSEMPLER Programmet nedenfor demonstrerer brugen af userfaultfd-mekanismen. Programmet opretter to trade, en fungerer som side-fejl-handteringen 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 oversaettelse, hvis sidefejl vil blive handteret via userfaultfd. Efter oprettelse af et userfaultfd-objekt opretter programmet en anoynm privat oversaettelse af den angivne storrelse og registerer adresseintervallet for den oversaettelse via operationen UFFDIO_REGISTER ioctl(2). Programmet opretter sa endnu en trad, der vil udfore opgaven med at handtere sidefejl. Hovedtraden lober sa igennem siderne for oversaettelsen ved at hente byte fra successive sider. Da siderne endnu ikke er blevet tilgaet vil den forste adgang af en byte i hver side udlose en side-fejl-haendelse pa userfaultfd-fildeskriptoren. Hver af side-fejl-haendelserne handteres af den anden trad, der befinder sig i en lokke, der behandler data fra userfaultfd-fildeskriptoren. I hver lokke-gennemgang, kalder den anden trad forst poll(2) for at tjekke tilstanden for fildeskriptoren, og laeser sa en haendelse fra fildeskriptoren. Alle sadanne haendelser bor vaere UFFD_EVENT_PAGEFAULT-haendelser, som traden handterer ved at kopiere en side med data ind i den fejlende region via operationen UFFDIO_COPY ioctl(2). Det folgende er et eksempel pa hvad vi ser, nar 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 #include #include #include #include #include #include #include #include #include #include #include #include #include static int page_size; static void * fault_handler_thread(void *arg) { long uffd; /* userfaultfd-fildeskriptor */ static int fault_cnt = 0; /* Fejlantal handteret indtil videre */ static char *page = NULL; static struct uffd_msg msg; /* Data laest 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"); } /* Lokke, der handterer indgaende haendelser pa userfaultfd- fildeskriptoren. */ for (;;) { int nready; ssize_t nread; struct pollfd pollfd; struct uffdio_copy uffdio_copy; /* Se hvad poll() fortaeller 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); /* Laes en haendelse 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 haendelse; verificer den antagelse. */ if (msg.event != UFFD_EVENT_PAGEFAULT) { fprintf(stderr, "Unexpected event on userfaultfd\n"); exit(EXIT_FAILURE); } /* Vis info om side-fejl-haendelsen. */ printf(" UFFD_EVENT_PAGEFAULT event: "); printf("flags = %"PRIx64"; ", msg.arg.pagefault.flags); printf("address = %"PRIx64"\n", msg.arg.pagefault.address); /* Kopier siden peget mod af 'page' ind i den fejlende region. Varier indholdet der kopieres ind, sa det er mere indlysende at hver fejl handteres separat. */ memset(page, 'A' + fault_cnt % 20, page_size); fault_cnt++; uffdio_copy.src = (unsigned long) page; /* Vi skal handtere sidefejl i sideenheder(!). Sa afrund fejlende adresse ned til sidegraensen. */ 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 *addr; /* Start pa region handteret af userfaultfd */ long uffd; /* userfaultfd-fildeskriptor */ size_t size, i; /* Regionstorrelse handteret af userfaultfd */ pthread_t thr; /* Id for trad der handterer 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"); /* BEMAERK: To-trins funktionen handshake (handtryk) er ikke kraevet her da dette eksempel ikke kraever nogle specifikke funktioner. Programmer der *skal* dette bor kalde UFFDIO_API to gange: en gang med >>features = 0<< for at registrere funktioner understottet af denne kerne og igen med undersaettet af funktionerne programmet faktisk onsker 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 oversaettelse. Hukommelsen vil vaere demand-zero paged - det vil side enndu ikke allokeret. Nar vi reelt rorer hukommelsen, sa 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 oversaettelsen vi lige oprettede til at blive handteret 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 trad der vil behandle userfaultfd-haendelser. */ s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd); if (s != 0) { errc(EXIT_FAILURE, s, "pthread_create"); } /* Hovedtraden rorer nu hukommelse i oversaettelsen, placeringer rores 1024 byte fra hinanden. Dette vil udlose userfaultfd-haendelser for alle sider i regionen. */ i = 0xf; /* Sikr at den fejlende adresse ikke er pa en sidegraense, for at teste, at vi handterede tilfaeldet 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); /* Gor tingene lidt langsommere */ } exit(EXIT_SUCCESS); } SE OGSA fcntl(2), ioctl(2), ioctl_userfaultfd(2), madvise(2), mmap(2) Documentation/admin-guide/mm/userfaultfd.rst i Linux-kernekildetraeet OVERSAETTELSE Oversaettere af denne manual til dansk Joe Hansen Denne oversaettelse er gratis dokumentation; laes GNU General Public License version 3 eller nyere for ophavsretbetingelser. Der er INGEN ANSVAR. Hvis du stoder pa fejl i oversaettelsen af denne vejledning, skal du sende en besked til . Linux man-pages 6.17 8. februar 2026 userfaultfd(2)