前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >示例展示虚拟内存和物理内存的分配

示例展示虚拟内存和物理内存的分配

作者头像
KINGYT
发布2019-07-08 19:04:08
1.5K0
发布2019-07-08 19:04:08
举报

通过前两篇文章(系统调用mmap的内核实现分析Linux下Page Fault的处理流程)我们可以知道,虚拟内存是在我们向操作系统申请内存(比如malloc或mmap)时分配的,而物理内存是在我们使用(比如读或写)虚拟内存时通过page fault分配的。

不管是虚拟内存的分配还是物理内存的分配,都是以page为单位的,page的默认大小为4096。

之前的两篇文章理论和代码部分比较多,所以,现在我们用示例的形式,展示下虚拟内存和物理内存的分配。

先看下面的这个小程序:

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  char *p;
  printf("1\n");
  sleep(10);

  p = mmap(NULL, 4096 + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (p == MAP_FAILED) {
    perror("mmap");
    return -1;
  }

  printf("2: %p\n", p);
  sleep(10);

  *p = 1;
  printf("3: %p\n", p);
  sleep(10);

  p += 4096;
  *p = 2;
  printf("4: %p\n", p);
  sleep(10);

  return 0;
}

编译并执行,结果为:

$ gcc main.c && ./a.out
1
2: 0x7efea597d000
3: 0x7efea597d000
4: 0x7efea597e000

在输出1、2、3、4时,分别用pmap命令查看当时的内存分配情况,对应到下面的四次输出:

$ pmap -x $(pgrep a.out)
7018:   ./a.out
Address           Kbytes     RSS   Dirty Mode  Mapping
0000564744227000       4       4       0 r---- a.out
0000564744228000       4       4       0 r-x-- a.out
0000564744229000       4       4       0 r---- a.out
000056474422a000       4       4       4 r---- a.out
000056474422b000       4       4       4 rw--- a.out
0000564745373000     132       4       4 rw---   [ anon ]
00007efea579e000     148     140       0 r---- libc-2.29.so
00007efea57c3000    1320     748       0 r-x-- libc-2.29.so
00007efea590d000     292      64       0 r---- libc-2.29.so
00007efea5956000       4       0       0 ----- libc-2.29.so
00007efea5957000      12      12      12 r---- libc-2.29.so
00007efea595a000      12      12      12 rw--- libc-2.29.so
00007efea595d000      24      16      16 rw---   [ anon ]
00007efea597f000       8       8       0 r---- ld-2.29.so
00007efea5981000     124     124       0 r-x-- ld-2.29.so
00007efea59a0000      32      32       0 r---- ld-2.29.so
00007efea59a9000       4       4       4 r---- ld-2.29.so
00007efea59aa000       4       4       4 rw--- ld-2.29.so
00007efea59ab000       4       4       4 rw---   [ anon ]
00007ffff006d000     132      12      12 rw---   [ stack ]
00007ffff00ee000      12       0       0 r----   [ anon ]
00007ffff00f1000       4       4       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB            2288    1208      76

$ pmap -x $(pgrep a.out)
7018:   ./a.out
Address           Kbytes     RSS   Dirty Mode  Mapping
0000564744227000       4       4       0 r---- a.out
0000564744228000       4       4       0 r-x-- a.out
0000564744229000       4       4       0 r---- a.out
000056474422a000       4       4       4 r---- a.out
000056474422b000       4       4       4 rw--- a.out
0000564745373000     132       4       4 rw---   [ anon ]
00007efea579e000     148     140       0 r---- libc-2.29.so
00007efea57c3000    1320     940       0 r-x-- libc-2.29.so
00007efea590d000     292     128       0 r---- libc-2.29.so
00007efea5956000       4       0       0 ----- libc-2.29.so
00007efea5957000      12      12      12 r---- libc-2.29.so
00007efea595a000      12      12      12 rw--- libc-2.29.so
00007efea595d000      24      16      16 rw---   [ anon ]
00007efea597d000       8       0       0 rw---   [ anon ]
00007efea597f000       8       8       0 r---- ld-2.29.so
00007efea5981000     124     124       0 r-x-- ld-2.29.so
00007efea59a0000      32      32       0 r---- ld-2.29.so
00007efea59a9000       4       4       4 r---- ld-2.29.so
00007efea59aa000       4       4       4 rw--- ld-2.29.so
00007efea59ab000       4       4       4 rw---   [ anon ]
00007ffff006d000     132      12      12 rw---   [ stack ]
00007ffff00ee000      12       0       0 r----   [ anon ]
00007ffff00f1000       4       4       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB            2296    1464      76

$ pmap -x $(pgrep a.out)
7018:   ./a.out
Address           Kbytes     RSS   Dirty Mode  Mapping
0000564744227000       4       4       0 r---- a.out
0000564744228000       4       4       0 r-x-- a.out
0000564744229000       4       4       0 r---- a.out
000056474422a000       4       4       4 r---- a.out
000056474422b000       4       4       4 rw--- a.out
0000564745373000     132       4       4 rw---   [ anon ]
00007efea579e000     148     140       0 r---- libc-2.29.so
00007efea57c3000    1320     940       0 r-x-- libc-2.29.so
00007efea590d000     292     128       0 r---- libc-2.29.so
00007efea5956000       4       0       0 ----- libc-2.29.so
00007efea5957000      12      12      12 r---- libc-2.29.so
00007efea595a000      12      12      12 rw--- libc-2.29.so
00007efea595d000      24      16      16 rw---   [ anon ]
00007efea597d000       8       4       4 rw---   [ anon ]
00007efea597f000       8       8       0 r---- ld-2.29.so
00007efea5981000     124     124       0 r-x-- ld-2.29.so
00007efea59a0000      32      32       0 r---- ld-2.29.so
00007efea59a9000       4       4       4 r---- ld-2.29.so
00007efea59aa000       4       4       4 rw--- ld-2.29.so
00007efea59ab000       4       4       4 rw---   [ anon ]
00007ffff006d000     132      12      12 rw---   [ stack ]
00007ffff00ee000      12       0       0 r----   [ anon ]
00007ffff00f1000       4       4       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB            2296    1468      80

$ pmap -x $(pgrep a.out)
7018:   ./a.out
Address           Kbytes     RSS   Dirty Mode  Mapping
0000564744227000       4       4       0 r---- a.out
0000564744228000       4       4       0 r-x-- a.out
0000564744229000       4       4       0 r---- a.out
000056474422a000       4       4       4 r---- a.out
000056474422b000       4       4       4 rw--- a.out
0000564745373000     132       4       4 rw---   [ anon ]
00007efea579e000     148     140       0 r---- libc-2.29.so
00007efea57c3000    1320     940       0 r-x-- libc-2.29.so
00007efea590d000     292     128       0 r---- libc-2.29.so
00007efea5956000       4       0       0 ----- libc-2.29.so
00007efea5957000      12      12      12 r---- libc-2.29.so
00007efea595a000      12      12      12 rw--- libc-2.29.so
00007efea595d000      24      16      16 rw---   [ anon ]
00007efea597d000       8       8       8 rw---   [ anon ]
00007efea597f000       8       8       0 r---- ld-2.29.so
00007efea5981000     124     124       0 r-x-- ld-2.29.so
00007efea59a0000      32      32       0 r---- ld-2.29.so
00007efea59a9000       4       4       4 r---- ld-2.29.so
00007efea59aa000       4       4       4 rw--- ld-2.29.so
00007efea59ab000       4       4       4 rw---   [ anon ]
00007ffff006d000     132      12      12 rw---   [ stack ]
00007ffff00ee000      12       0       0 r----   [ anon ]
00007ffff00f1000       4       4       0 r-x--   [ anon ]
---------------- ------- ------- -------
total kB            2296    1472      84

当程序输出1时,此时第一次pmap命令的输出就是该程序的原始内存情况。

当程序输出2时,此时已经执行过mmap操作,对应的第二次pmap命令的输出比第一次多了一个[ anon ]区域(第45行),该区域的起始地址也正好和程序输出2时输出的地址相同,说明该区域就是我们用mmap分配的内存区域。

该区域的虚拟内存大小是8k,因为我们在调用mmap时指定的内存大小是4097,page对齐后正好是8k。

该区域的物理内存大小是0,因为我们还没使用过该区域。

当程序输出3时,此时我们已经对p对应的地址空间赋值,也就是使用了虚拟内存的第一个page,对应看pmap命令的第三次输出,此时的[ anon ]区域(第74行)显示物理内存已使用4k。

当程序输出4时,此时我们已经对虚拟内存的第二个page进行了写操作,对应看pmap命令的第四次输出,此时的[ anon ]区域(第103行)显示已使用的物理内存是8k。

通过上面的示例程序和pmap命令,我们可以清楚的看到,进程的虚拟内存和物理内存是何时分配的。

那如何确定物理内存的分配是page fault触发的呢?

这里我们介绍一个非常强大的工具bpftrace,它使用了内核的ebpf特性,使得我们可以动态监控内核里的方法调用。

工具的详细描述请看下面的地址:

https://github.com/iovisor/bpftrace

下面结合上面的例子,展示下该工具的使用:

sudo bpftrace -e 'kprobe:handle_mm_fault /comm == "a.out"/ { printf("addr: 0x%lx\n", arg1); }'

该命令的意思是,监控内核的handle_mm_fault方法,如果调用该方法的程序是a.out,则输出该方法第二个参数的值。

由上一篇文章我们可以知道,handle_mm_fault方法就是被do_page_fault调用的方法,方法内容大致如下:

// mm/memory.c
vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
                unsigned int flags)
{
        ...
}

由此可见,我们输出的第二个参数,就是触发page fault的虚拟内存地址。

好,看下bpftrace命令的输出:

$ sudo bpftrace -e 'kprobe:handle_mm_fault /comm == "a.out"/ { printf("addr: 0x%lx\n", arg1); }'
...
addr: 0x7efea597d000
addr: 0x7efea597e000
...

由上可见,bpftrace命令输出的page fault触发地址,正是我们的程序在输出3、4时输出的地址。

由此可见,示例程序中的那两次赋值操作,触发了page fault,进而分配了物理内存。

再推荐下我们之前推荐过的一篇文章,讲的也是linux内核对进程内存的分配、管理等,相信这次你会更加理解这篇文章。

https://manybutfinite.com/post/how-the-kernel-manages-your-memory/

完。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档