mremap:用户态调用mremap后VMA的pgoff以及page会发生发生

张开发
2026/4/16 3:49:08 15 分钟阅读

分享文章

mremap:用户态调用mremap后VMA的pgoff以及page会发生发生
结论先说结论1、执行mremap后如果新的addr之前被映射过之前映射过的page会被释放掉新的addr先unmap掉。重新把旧addr的page重新映射到新的addr2、VMA 确实会发生变化如果原来的一个连续 VMA地址 会被拆分成两个独立的 VMA中间出现一段未映射的地址空隙。那么就会新建一个vma。3、旧的page内容不会变page-index(pgoff)也不会变。变的只有VMA的 vm_start和vm_pgoff.4、反向映射时的地址计算公式永远成立用户态的虚拟地址 vma-vm_start (page-index - vma-vm_pgoff) * PAGE_SIZE背景我们知道mremap可以改变一个已申请内存区域的映射地址范围例如原先申请的地址范围是A到Alen我们可以改变为B到Blen2。可是你知道这样改变后VMA会发生什么变化吗VMA可能会拆成新的VMA但是page呢page有可能不变那这时反向映射RMAP是如何能通过page再次找到映射这个page的新VMA呢这个问题来源一个ksm优化引发的讨论https://lore.kernel.org/all/a14a89ba-e870-47d2-a903-564332da9877kernel.org/实验方法带着这个疑问咱们做一个实验用户态调用mremap。在内核态的do_mremap函数中的入口和出口打印出VMA以及对应page的信息。用户态编写这样一个C程序433 /* To test if ksm page can be migrated when its mremapped */ 434 int merge_mremap_and_migrate(struct global_data *data) 435 { 436 int ret 0; 437 /* Allocate range and set the same data */ 438 >diff --git a/include/linux/mm.h b/include/linux/mm.h index 13336340612e..818e0eb2bb2c 100644 --- a/include/linux/mm.h b/include/linux/mm.h -4200,6 4200,10 static inline vm_fault_t vmf_error(int err) return VM_FAULT_SIGBUS; } struct page *follow_page_mask(struct vm_area_struct *vma, unsigned long address, unsigned int foll_flags, unsigned long *page_mask); /* * Convert errno to return value for -page_mkwrite() calls. * diff --git a/mm/gup.c b/mm/gup.c index 8e7dc2c6ee73..a0b13934c7be 100644 --- a/mm/gup.c b/mm/gup.c -1004,7 1004,7 static struct page *follow_p4d_mask(struct vm_area_struct *vma, * an error pointer if there is a mapping to something not represented * by a page descriptor (see also vm_normal_page()). */ -static struct page *follow_page_mask(struct vm_area_struct *vma, struct page *follow_page_mask(struct vm_area_struct *vma, unsigned long address, unsigned int flags, unsigned long *page_mask) { diff --git a/mm/mremap.c b/mm/mremap.c index 2be876a70cc0..eacfd844b9ad 100644 --- a/mm/mremap.c b/mm/mremap.c -1912,6 1912,27 static unsigned long remap_move(struct vma_remap_struct *vrm) return res; } static void print_vma_info(struct vm_area_struct *vma, unsigned long address, char *prefix) { /* * address: you want to lookup page, the address is the mremap()s first * argument. */ struct page *page; unsigned long page_mask 0; page follow_page_mask(vma, address, FOLL_GET, page_mask); if (!page) { pr_err(mremap you pass in an error address(no page) at %lx\n, address); return; } printk(%s vm_start:%lx vm_pgeff:%lx users given address: %lx page-index:%lx\n, prefix, vma-vm_start, vma-vm_pgoff, address, folio_pgoff(page_folio(page))); } static unsigned long do_mremap(struct vma_remap_struct *vrm) { struct mm_struct *mm current-mm; -1930,9 1951,12 static unsigned long do_mremap(struct vma_remap_struct *vrm) vrm-mmap_locked true; if (vrm_move_only(vrm)) { vrm-vma vma_lookup(current-mm, vrm-addr); print_vma_info(vrm-vma, vrm-addr, Before meremap move_only); res remap_move(vrm); } else { vrm-vma vma_lookup(current-mm, vrm-addr); print_vma_info(vrm-vma, vrm-addr, Before meremap ); res check_prep_vma(vrm); if (res) goto out; -1944,6 1968,13 static unsigned long do_mremap(struct vma_remap_struct *vrm) out: failed IS_ERR_VALUE(res); if (!failed) { struct vm_area_struct *new_vma vma_lookup(current-mm, vrm-new_addr); print_vma_info(new_vma, vrm-new_addr, After meremap new address); } else{ pr_err(mremap failed\n); } if (vrm-mmap_locked) mmap_write_unlock(mm);实验结果【用户态打印】Before meremap region: 0x7f0f52e20000 【内核态】 [ 26.544965] Before meremap move_only vm_start:7f0f52e20000 vm_pgeff:7f0f52e20 usersgiven address: 7f0f52e21000 page-index:7f0f52e21 【内核态】 [ 26.548166] After meremap new address vm_start:7f0f52e22000 vm_pgeff:7f0f52e21 usersgiven address: 7f0f52e22000 page-index:7f0f52e21 【用户态】After meremap region: 0x7f0f52e22000

更多文章