Userfaults allow the implementation of on-demand paging from userland and more generally they allow userland to take control of various memory page faults, something otherwise only the kernel code could do.
/* Register the memory range of the mapping we just created for handling by the userfaultfd object. In mode, we request to track missing pages (i.e., pages that have not yet been faulted in). */
/* Copy the page pointed to by 'page' into the faulting region. Vary the contents that are copied in, so that it is more obvious that each fault is handled separately. */
/* userfaultfd_demo.c Licensed under the GNU General Public License version 2 or later. */ #define _GNU_SOURCE #include<inttypes.h> #include<sys/types.h> #include<stdio.h> #include<linux/userfaultfd.h> #include<pthread.h> #include<errno.h> #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #include<signal.h> #include<poll.h> #include<string.h> #include<sys/mman.h> #include<sys/syscall.h> #include<sys/ioctl.h> #include<poll.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0)
staticint page_size;
staticvoid * fault_handler_thread(void *arg) { staticstructuffd_msgmsg;/* Data read from userfaultfd */ staticint fault_cnt = 0; /* Number of faults so far handled */ long uffd; /* userfaultfd file descriptor */ staticchar *page = NULL; structuffdio_copyuffdio_copy; ssize_t nread;
uffd = (long) arg;
/* Create a page that will be copied into the faulting region. */
/* Copy the page pointed to by 'page' into the faulting region. Vary the contents that are copied in, so that it is more obvious that each fault is handled separately. */
printf(" (uffdio_copy.copy returned %"PRId64")\n", uffdio_copy.copy); } }
int main(int argc, char *argv[]) { long uffd; /* userfaultfd file descriptor */ char *addr; /* Start of region handled by userfaultfd */ uint64_t len; /* Length of region handled by userfaultfd */ pthread_t thr; /* ID of thread that handles page faults */ structuffdio_apiuffdio_api; structuffdio_registeruffdio_register; int s;
/* Create a private anonymous mapping. The memory will be demand-zero paged--that is, not yet allocated. When we actually touch the memory, it will be allocated via the userfaultfd. */
printf("Address returned by mmap() = %p\n", addr);
/* Register the memory range of the mapping we just created for handling by the userfaultfd object. In mode, we request to track missing pages (i.e., pages that have not yet been faulted in). */
/* Create a thread that will process the userfaultfd events. */
s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd); if (s != 0) { errno = s; errExit("pthread_create"); }
/* Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will trigger userfaultfd events for all pages in the region. */
int l; l = 0xf; /* Ensure that faulting address is not on a page boundary, in order to test that we correctly handle that case in fault_handling_thread(). */ while (l < len) { char c = addr[l]; printf("Read address %p in main(): ", addr + l); printf("%c\n", c); l += 1024; usleep(100000); /* Slow things down a little */ }