MMAP接口实现原理

一、背景

mmap接口是很常用的用户态内存映射接口,大部分时候用于映射一个文件,也可以映射一块内存。

#include <sys/mman.h>
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void *addr, size_t len);

二、linux内核实现机制

1、mmap接口

对应的内核实现接口是do_mmap接口,这个接口内部主要实现了两部分:

1)从进程地址空间找到一个可用的地址;分配一个vma负责该地址区间(有相邻地址,可以合并到原有vma)

2)初始化配置vma,重点关注vma->vm_ops:

如果是文件映射,则指向相应file的vm_ops;

如果是匿名映射,vm_ops为空。

vm_ops包含了很多回调函数,常用的就是几种,典型的就是page fault接口。以fuse文件系统的vm_ops为例说明:

static const struct vm_operations_struct fuse_file_vm_ops = {
	.close		= fuse_vma_close,
	.fault		= filemap_fault,
	.map_pages	= filemap_map_pages,
	.page_mkwrite	= fuse_page_mkwrite,
};

mmap里只是分配了地址空间和相应的地址管理单元vma,并不涉及真实的物理页面分配,创建页表映射等。根据按需分配的准则,真实的页面分配、文件数据准备、页表建立都是在缺页异常中处理的。

2、缺页异常处理

缺页异常是X86架构的第14号中断(异常),在内核初始化阶段,初始化IDT表时配置缺页异常处理接口。

static const __initconst struct idt_data early_pf_idts[] = {
	INTG(X86_TRAP_PF,		asm_exc_page_fault),        //缺页异常中断
};
void __init idt_setup_early_pf(void)
{
	idt_setup_from_table(idt_table, early_pf_idts,
			     ARRAY_SIZE(early_pf_idts), true);
}

异常处理入口函数asm_exec_page_fault如下。

DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault)
{
	unsigned long address = read_cr2();
	irqentry_state_t state;

	prefetchw(&current->mm->mmap_lock);

	/*
	 * Entry handling for valid #PF from kernel mode is slightly
	 * different: RCU is already watching and rcu_irq_enter() must not
	 * be invoked because a kernel fault on a user space address might
	 * sleep.
	 *
	 * In case the fault hit a RCU idle region the conditional entry
	 * code reenabled RCU to avoid subsequent wreckage which helps
	 * debuggability.
	 */
	state = irqentry_enter(regs);

	instrumentation_begin();
	handle_page_fault(regs, error_code, address); //page fault处理接口
	instrumentation_end();

	irqentry_exit(regs, state);
}

handle_page_fault接口实现如下。

static __always_inline void
handle_page_fault(struct pt_regs *regs, unsigned long error_code,
			      unsigned long address)
{
	trace_page_fault_entries(regs, error_code, address);

	if (unlikely(kmmio_fault(regs, address)))
		return;

	/* Was the fault on kernel-controlled part of the address space? */
	if (unlikely(fault_in_kernel_space(address))) {
		do_kern_addr_fault(regs, error_code, address);  //内核地址缺页处理
	} else {
		do_user_addr_fault(regs, error_code, address);    //用户态地址缺页处理
		/*
		 * User address page fault handling might have reenabled
		 * interrupts. Fixing up all potential exit points of
		 * do_user_addr_fault() and its leaf functions is just not
		 * doable w/o creating an unholy mess or turning the code
		 * upside down.
		 */
		local_irq_disable();
	}
}

因为mmap流程就是对应用户态的地址映射,所以我们看一下用户态地址的缺页处理接口do_user_addr_fault().这个接口里的处理路径很长,最终如果判断是有效的访问,则会执行handle_mm_fault接口,这个接口看起来比较熟悉。

static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
		unsigned long address, unsigned int flags)
{
    //遍历4级页表项,如果页表缺页,分配和填充每一级页表项
    //最后处理内存地址缺页
    return handle_pte_fault(&vmf);
}

static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
{
	if (!vmf->pte) {    //pte为空
		if (vma_is_anonymous(vmf->vma))    //vma->vm_ops为空,判定匿名映射
			return do_anonymous_page(vmf);  //匿名映射缺页处理
		else
			return do_fault(vmf);    //文件映射缺页处理
	}
}

2.2.1 PTE为空

如果是PTE页表项没有建立,执行handle_pte_fault路径,会调用到do_fault,就会调用vma->vm_ops->fault接口。

1、文件系统,以fuse文件系统为例,filemap_fault

1)pagecache_get_page,从page cache中分配一个页面或返回已有的页面;

2)filemap_read_page,读取文件映射页面;

2、设备驱动

如果是映射MMIO地址空间,就会做ioremap操作,比如vfio_pci_mmap_fault。

2.2.2 PTE不为空

需要进一步区分几种情况,如页面是否已经换出、是否是访问权限不匹配等分别作不同处理。
(1)do_numa_page
由于各个节点的物理页不平衡,并且判断vma结构体是可以操作的,说明物理页再其他节点中,需要把物理页从其他节点中国移回来到目前的节点内。
(2)do_swap_page
由于匿名物理页面被回收了,所以进程再次访问一块虚拟地址时,就会产生缺页中断,最终进入到 do_swap_page,在这个函数中会重新分配新的页面,然后再从swap分区读回这块虚拟地址对应的数据。
(3)do_wp_page
写时复制一般发生在父子进程之间,fork时copy_mm时会将父进程和子进程的页表项都设置为只读,无论是父进程或子进程执行写入都会触发page fault,通过此函数执行写时复制,分配新的page,拷贝旧page到新page, 并修改相应的页表项为读写。参数vmf->vma保存了线性区原有的读写权限。

linux内核内存管理-缺页异常 - 知乎

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值