从内核到用户空间(1) — 用户态缺页处理机制 userfaultfd 的使用
近年,一些本为内核处理的任务,分别出现用户态的实现,有的是为了提升开发灵活性(FUSE、userfaultfd),有的则是为了提高与外设通信的性能(SPDK、DPDK)。本系列文章对我所了解到的用户空间实现的内核机制进行使用介绍或原理分析。第一篇文章介绍用户态的缺页处理 — userfaultfd机制,以后还可能根据我的学习进度介绍userfaultfd的内核实现原理、FUSE的使用和原理、SPDK等内容。文章若有错误,恳请指正。 userfaultfd 机制让在用户控制缺页处理提供可能,进程可以在用户空间为自己的程序定义page fault handler,增加了灵活性,但也可能由于类似FUSE之于内核FS的问题(调用层次加深)而影响性能。 1. 基本使用步骤 以最基本的用户空间进行匿名页缺页处理为例,(例子代码基本来自userfaultfd的man page[1],)步骤大致如下: STEP 1. 创建一个描述符uffd 要使用此功能,首先应该用userfaultfd调用[1]来创建一个fd,例如: // userfaultfd系统调用创建并返回一个uffd,类似一个文件的fd uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 然后,所有的注册内存区间、配置和最终的缺页处理等就都需要用ioctl来对这个uffd操作。ioctl-userfaultfd[2]支持UFFDIO_API、UFFDIO_REGISTER、UFFDIO_UNREGISTER、UFFDIO_COPY、UFFDIO_ZEROPAGE、UFFDIO_WAKE等选项。比如UFFDIO_REGISTER用来向userfaultfd机制注册一个监视区域,这个区域发生缺页时,需要用UFFDIO_COPY来向缺页的地址拷贝自定义数据。 STEP 2. 用ioctl的UFFDIO_REGISTER选项注册监视区域 比如,UFFDIO_REGISTER对应的注册操作如下: // 注册时要用一个struct uffdio_register结构传递注册信息: // struct uffdio_range { // __u64 start; /* Start of range */ // __u64 len; /* Length of range (bytes) */ // }; // […]