mmap 的内核实现
mmap 的内核实现
延时分配
参考如下简单的
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
void *p;
sleep(5);
p = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED) {
perror("mmap");
return -1;
}
printf("%p\n", p);
sleep(5);
return 0;
}
执行该程序,输出
$ ./a.out
0x7f521d667000
$ pmap -x $(pgrep a.out)
32408: ./a.out
Address Kbytes RSS Dirty Mode Mapping
0000555bb0511000 4 4 0 r---- a.out
0000555bb0512000 4 4 0 r-x-- a.out
0000555bb0513000 4 0 0 r---- a.out
0000555bb0514000 4 4 4 r---- a.out
0000555bb0515000 4 4 4 rw--- a.out
00007f521d45e000 148 140 0 r---- libc-2.29.so
00007f521d483000 1320 628 0 r-x-- libc-2.29.so
00007f521d5cd000 292 64 0 r---- libc-2.29.so
00007f521d616000 4 0 0 ----- libc-2.29.so
00007f521d617000 12 12 12 r---- libc-2.29.so
00007f521d61a000 12 12 12 rw--- libc-2.29.so
00007f521d61d000 24 16 16 rw--- [ anon ]
00007f521d63e000 8 8 0 r---- ld-2.29.so
00007f521d640000 124 124 0 r-x-- ld-2.29.so
00007f521d65f000 32 32 0 r---- ld-2.29.so
00007f521d668000 4 4 4 r---- ld-2.29.so
00007f521d669000 4 4 4 rw--- ld-2.29.so
00007f521d66a000 4 4 4 rw--- [ anon ]
00007fffd1e55000 132 12 12 rw--- [ stack ]
00007fffd1f04000 12 0 0 r---- [ anon ]
00007fffd1f07000 4 4 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 2156 1080 72
$ pmap -x $(pgrep a.out)
32408: ./a.out
Address Kbytes RSS Dirty Mode Mapping
0000555bb0511000 4 4 0 r---- a.out
0000555bb0512000 4 4 0 r-x-- a.out
0000555bb0513000 4 4 0 r---- a.out
0000555bb0514000 4 4 4 r---- a.out
0000555bb0515000 4 4 4 rw--- a.out
0000555bb1b7a000 132 4 4 rw--- [ anon ]
00007f521d45e000 148 140 0 r---- libc-2.29.so
00007f521d483000 1320 948 0 r-x-- libc-2.29.so
00007f521d5cd000 292 128 0 r---- libc-2.29.so
00007f521d616000 4 0 0 ----- libc-2.29.so
00007f521d617000 12 12 12 r---- libc-2.29.so
00007f521d61a000 12 12 12 rw--- libc-2.29.so
00007f521d61d000 24 16 16 rw--- [ anon ]
00007f521d63e000 8 8 0 r---- ld-2.29.so
00007f521d640000 124 124 0 r-x-- ld-2.29.so
00007f521d65f000 32 32 0 r---- ld-2.29.so
00007f521d667000 4 0 0 rw--- [ anon ]
00007f521d668000 4 4 4 r---- ld-2.29.so
00007f521d669000 4 4 4 rw--- ld-2.29.so
00007f521d66a000 4 4 4 rw--- [ anon ]
00007fffd1e55000 132 12 12 rw--- [ stack ]
00007fffd1f04000 12 0 0 r---- [ anon ]
00007fffd1f07000 4 4 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 2292 1472 76
在
实际物理内存占用为什么是
按页分配
再看下上面的源码,我们指定的内存长度明明是
// arch/x86/kernel/sys_x86_64.c
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags,
unsigned long, fd, unsigned long, off)
{
long error;
error = -EINVAL;
if (off & ~PAGE_MASK)
goto out;
error = ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:
return error;
}
该方法调用了
// mm/mmap.c
unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
struct file *file = NULL;
unsigned long retval;
if (!(flags & MAP_ANONYMOUS)) {
...
file = fget(fd);
...
}
...
retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
...
return retval;
}
该方法又调用了
// mm/util.c
unsigned longvm_mmap_pgoff(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flag, unsigned long pgoff)
{
unsigned long ret;
struct mm_struct *mm = current->mm;
...
if (!ret) {
...
ret = do_mmap_pgoff(file, addr, len, prot, flag, pgoff,
&populate, &uf);
...
}
return ret;
}
该方法又调用了
// include/linux/mm.h
static inline unsigned long
do_mmap_pgoff(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot, unsigned long flags,
unsigned long pgoff, unsigned long *populate,
struct list_head *uf)
{
return do_mmap(file, addr, len, prot, flags, 0, pgoff, populate, uf);
}
该方法又调用了
// mm/mmap.c
unsigned long do_mmap(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flags, vm_flags_t vm_flags,
unsigned long pgoff, unsigned long *populate,
struct list_head *uf)
{
struct mm_struct *mm = current->mm;
...
len = PAGE_ALIGN(len);
...
addr = get_unmapped_area(file, addr, len, pgoff, flags);
...
addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
...
return addr;
}
该方法先用宏
之后,该方法又调用了
// mm/mmap.c
unsigned long mmap_region(struct file *file, unsigned long addr,
unsigned long len, vm_flags_t vm_flags, unsigned long pgoff,
struct list_head *uf)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
...
vma = vm_area_alloc(mm);
...
vma->vm_start = addr;
vma->vm_end = addr + len;
vma->vm_flags = vm_flags;
vma->vm_page_prot = vm_get_page_prot(vm_flags);
vma->vm_pgoff = pgoff;
if (file) {
...
vma->vm_file = get_file(file);
error = call_mmap(file, vma);
...
} else if (vm_flags & VM_SHARED) {
...
} else {
vma_set_anonymous(vma);
}
vma_link(mm, vma, prev, rb_link, rb_parent);
...
return addr;
...
}
该方法先调用
// include/linux/fs.h
static inline int call_mmap(struct file *file, struct vm_area_struct *vma)
{
return file->f_op->mmap(file, vma);
}
该方法又调用了
// fs/ext4/file.c
static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
{
...
if (IS_DAX(file_inode(file))) {
...
} else {
vma->vm_ops = &ext4_file_vm_ops;
}
return 0;
}
该方法的作用是初始化
// include/linux/mm.h
static inline void vma_set_anonymous(struct vm_area_struct *vma)
{
vma->vm_ops = NULL;
}
该方法将