内核解读之内存管理(12)进程虚拟内存管理 vm_area_struct 与反向映射

news2024/11/18 20:47:12

在32位的系统上,线性地址空间可达到4GB,这4GB一般按照3:1的比例进行分配,也就是说用户进程享有前3GB线性地址空间,而内核独享最后1GB线性地址空间。由于虚拟内存的引入,每个进程都可拥有3GB的虚拟内存,并且用户进程之间的地址空间是互不可见、互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题。在前面介绍的一些分配内存的途径中,无论是伙伴系统中分配页的函数,还是slab分配器中分配对象的函数,它们都会尽量快速地响应内核的分配请求,将相应的内存提交给内核使用,而内核对待用户空间显然不能如此。用户空间动态申请内存时往往只是获得一块线性地址的使用权,而并没有将这块线性地址区域与实际的物理内存对应上,只有当用户空间真正操作申请的内存时,才会触发一次缺页异常,这时内核才会分配实际的物理内存给用户空间。

用户进程的虚拟地址空间包含了若干区域,这些区域的分布方式是特定于体系结构的,不过所有的方式都包含下列成分:

  • 可执行文件的二进制代码,也就是程序的代码段
  • 存储全局变量的数据段
  • 用于保存局部变量和实现函数调用的栈
  • 环境变量和命令行参数
  • 程序使用的动态库的代码
  • 用于映射文件内容的区域

img

cat /proc/<pid>/maps可以查看某一进程的所有映射区间。

$ sudo cat /proc/1/maps 
[sudo] password for developer: 
55725a3000-55726ce000 r-xp 00000000 b3:01 917543                         /lib/systemd/systemd
55726de000-5572718000 r--p 0012b000 b3:01 917543                         /lib/systemd/systemd
5572718000-5572719000 rw-p 00165000 b3:01 917543                         /lib/systemd/systemd
558e959000-558eb63000 rw-p 00000000 00:00 0                              [heap]
7f80000000-7f80021000 rw-p 00000000 00:00 0 
7f80021000-7f84000000 ---p 00000000 00:00 0 
7f88000000-7f88021000 rw-p 00000000 00:00 0 
7f88021000-7f8c000000 ---p 00000000 00:00 0 
7f8c7a9000-7f8c7aa000 ---p 00000000 00:00 0 
7f8c7aa000-7f8cfaa000 rw-p 00000000 00:00 0 
7f8cfaa000-7f8cfab000 ---p 00000000 00:00 0 
7f8cfab000-7f8d7ae000 rw-p 00000000 00:00 0 
7f8d7ae000-7f8d856000 r-xp 00000000 b3:01 918344                         /lib/aarch64-linux-gnu/libm-2.27.so
7f8d856000-7f8d865000 ---p 000a8000 b3:01 918344                         /lib/aarch64-linux-gnu/libm-2.27.so
7f8d865000-7f8d866000 r--p 000a7000 b3:01 918344                         /lib/aarch64-linux-gnu/libm-2.27.so
7f8d866000-7f8d867000 rw-p 000a8000 b3:01 918344                         /lib/aarch64-linux-gnu/libm-2.27.so
7f8d867000-7f8d880000 r-xp 00000000 b3:01 918341                         /lib/aarch64-linux-gnu/libudev.so.1.6.9
7f8d880000-7f8d88f000 ---p 00019000 b3:01 918341                         /lib/aarch64-linux-gnu/libudev.so.1.6.9
7f8d88f000-7f8d890000 r--p 00018000 b3:01 918341                         /lib/aarch64-linux-gnu/libudev.so.1.6.9
7f8d890000-7f8d891000 rw-p 00019000 b3:01 918341                         /lib/aarch64-linux-gnu/libudev.so.1.6.9
7f8d891000-7f8d8a2000 r-xp 00000000 b3:01 918337                         /lib/aarch64-linux-gnu/libgpg-error.so.0.22.0
7f8d8a2000-7f8d8b1000 ---p 00011000 b3:01 918337                         /lib/aarch64-linux-gnu/libgpg-error.so.0.22.0
7f8d8b1000-7f8d8b2000 r--p 00010000 b3:01 918337                         /lib/aarch64-linux-gnu/libgpg-error.so.0.22.0
7f8d8b2000-7f8d8b3000 rw-p 00011000 b3:01 918337                         /lib/aarch64-linux-gnu/libgpg-error.so.0.22.0
7f8d8b3000-7f8d8bc000 r-xp 00000000 b3:01 918236                         /lib/aarch64-linux-gnu/libjson-c.so.3.0.1
7f8d8bc000-7f8d8cb000 ---p 00009000 b3:01 918236                         /lib/aarch64-linux-gnu/libjson-c.so.3.0.1
7f8d8cb000-7f8d8cc000 r--p 00008000 b3:01 918236                         /lib/aarch64-linux-gnu/libjson-c.so.3.0.1
7f8d8cc000-7f8d8cd000 rw-p 00009000 b3:01 918236                         /lib/aarch64-linux-gnu/libjson-c.so.3.0.1
7f8d8cd000-7f8d8d4000 r-xp 00000000 b3:01 551785                         /usr/lib/aarch64-linux-gnu/libargon2.so.0
7f8d8d4000-7f8d8e3000 ---p 00007000 b3:01 551785                         /usr/lib/aarch64-linux-gnu/libargon2.so.0
7f8d8e3000-7f8d8e4000 r--p 00006000 b3:01 551785                         /usr/lib/aarch64-linux-gnu/libargon2.so.0
7f8d8e4000-7f8d8e5000 rw-p 00007000 b3:01 551785                         /usr/lib/aarch64-linux-gnu/libargon2.so.0
7f8d8e5000-7f8d94a000 r-xp 00000000 b3:01 918410                         /lib/aarch64-linux-gnu/libdevmapper.so.1.02.1
7f8d94a000-7f8d95a000 ---p 00065000 b3:01 918410                         /lib/aarch64-linux-gnu/libdevmapper.so.1.02.1
7f8d95a000-7f8d95b000 r--p 00065000 b3:01 918410                         /lib/aarch64-linux-gnu/libdevmapper.so.1.02.1
7f8d95b000-7f8d95f000 rw-p 00066000 b3:01 918410                         /lib/aarch64-linux-gnu/libdevmapper.so.1.02.1
7f8d95f000-7f8d960000 rw-p 00000000 00:00 0 
7f8d960000-7f8d964000 r-xp 00000000 b3:01 918353                         /lib/aarch64-linux-gnu/libattr.so.1.1.0
7f8d964000-7f8d973000 ---p 00004000 b3:01 918353                         /lib/aarch64-linux-gnu/libattr.so.1.1.0
7f8d973000-7f8d974000 r--p 00003000 b3:01 918353                         /lib/aarch64-linux-gnu/libattr.so.1.1.0
7f8d974000-7f8d975000 rw-p 00004000 b3:01 918353                         /lib/aarch64-linux-gnu/libattr.so.1.1.0
7f8d975000-7f8d979000 r-xp 00000000 b3:01 918310                         /lib/aarch64-linux-gnu/libcap-ng.so.0.0.0
7f8d979000-7f8d988000 ---p 00004000 b3:01 918310                         /lib/aarch64-linux-gnu/libcap-ng.so.0.0.0
7f8d988000-7f8d989000 r--p 00003000 b3:01 918310                         /lib/aarch64-linux-gnu/libcap-ng.so.0.0.0
7f8d989000-7f8d98a000 rw-p 00004000 b3:01 918310                         /lib/aarch64-linux-gnu/libcap-ng.so.0.0.0
7f8d98a000-7f8d990000 r-xp 00000000 b3:01 918406                         /lib/aarch64-linux-gnu/libuuid.so.1.3.0
7f8d990000-7f8d99f000 ---p 00006000 b3:01 918406                         /lib/aarch64-linux-gnu/libuuid.so.1.3.0
7f8d99f000-7f8d9a0000 r--p 00005000 b3:01 918406                         /lib/aarch64-linux-gnu/libuuid.so.1.3.0
7f8d9a0000-7f8d9a1000 rw-p 00006000 b3:01 918406                         /lib/aarch64-linux-gnu/libuuid.so.1.3.0
7f8d9a1000-7f8d9a4000 r-xp 00000000 b3:01 918240                         /lib/aarch64-linux-gnu/libdl-2.27.so
7f8d9a4000-7f8d9b4000 ---p 00003000 b3:01 918240                         /lib/aarch64-linux-gnu/libdl-2.27.so
7f8d9b4000-7f8d9b5000 r--p 00003000 b3:01 918240                         /lib/aarch64-linux-gnu/libdl-2.27.so
7f8d9b5000-7f8d9b6000 rw-p 00004000 b3:01 918240                         /lib/aarch64-linux-gnu/libdl-2.27.so
7f8d9b6000-7f8da17000 r-xp 00000000 b3:01 918323                         /lib/aarch64-linux-gnu/libpcre.so.3.13.3
7f8da17000-7f8da26000 ---p 00061000 b3:01 918323                         /lib/aarch64-linux-gnu/libpcre.so.3.13.3
7f8da26000-7f8da27000 r--p 00060000 b3:01 918323                         /lib/aarch64-linux-gnu/libpcre.so.3.13.3
7f8da27000-7f8da28000 rw-p 00061000 b3:01 918323                         /lib/aarch64-linux-gnu/libpcre.so.3.13.3
7f8da28000-7f8da3f000 r-xp 00000000 b3:01 918397                         /lib/aarch64-linux-gnu/libpthread-2.27.so
7f8da3f000-7f8da4e000 ---p 00017000 b3:01 918397                         /lib/aarch64-linux-gnu/libpthread-2.27.so
7f8da4e000-7f8da4f000 r--p 00016000 b3:01 918397                         /lib/aarch64-linux-gnu/libpthread-2.27.so
7f8da4f000-7f8da50000 rw-p 00017000 b3:01 918397                         /lib/aarch64-linux-gnu/libpthread-2.27.so
7f8da50000-7f8da54000 rw-p 00000000 00:00 0 
7f8da54000-7f8da6f000 r-xp 00000000 b3:01 550388                         /usr/lib/aarch64-linux-gnu/liblz4.so.1.7.1
7f8da6f000-7f8da7e000 ---p 0001b000 b3:01 550388                         /usr/lib/aarch64-linux-gnu/liblz4.so.1.7.1
7f8da7e000-7f8da7f000 r--p 0001a000 b3:01 550388                         /usr/lib/aarch64-linux-gnu/liblz4.so.1.7.1
7f8da7f000-7f8da80000 rw-p 0001b000 b3:01 550388                         /usr/lib/aarch64-linux-gnu/liblz4.so.1.7.1
7f8da80000-7f8da9f000 r-xp 00000000 b3:01 918356                         /lib/aarch64-linux-gnu/liblzma.so.5.2.2
7f8da9f000-7f8daae000 ---p 0001f000 b3:01 918356                         /lib/aarch64-linux-gnu/liblzma.so.5.2.2
7f8daae000-7f8daaf000 r--p 0001e000 b3:01 918356                         /lib/aarch64-linux-gnu/liblzma.so.5.2.2
7f8daaf000-7f8dab0000 rw-p 0001f000 b3:01 918356                         /lib/aarch64-linux-gnu/liblzma.so.5.2.2
7f8dab0000-7f8dae0000 r-xp 00000000 b3:01 918359                         /lib/aarch64-linux-gnu/libidn.so.11.6.16
7f8dae0000-7f8daf0000 ---p 00030000 b3:01 918359                         /lib/aarch64-linux-gnu/libidn.so.11.6.16
7f8daf0000-7f8daf1000 r--p 00030000 b3:01 918359                         /lib/aarch64-linux-gnu/libidn.so.11.6.16
7f8daf1000-7f8daf2000 rw-p 00031000 b3:01 918359                         /lib/aarch64-linux-gnu/libidn.so.11.6.16
7f8daf2000-7f8daf8000 r-xp 00000000 b3:01 553538                         /usr/lib/aarch64-linux-gnu/libip4tc.so.0.1.0
7f8daf8000-7f8db07000 ---p 00006000 b3:01 553538                         /usr/lib/aarch64-linux-gnu/libip4tc.so.0.1.0
7f8db07000-7f8db08000 r--p 00005000 b3:01 553538                         /usr/lib/aarch64-linux-gnu/libip4tc.so.0.1.0
7f8db08000-7f8db09000 rw-p 00006000 b3:01 553538                         /usr/lib/aarch64-linux-gnu/libip4tc.so.0.1.0
7f8db09000-7f8dbae000 r-xp 00000000 b3:01 918396                         /lib/aarch64-linux-gnu/libgcrypt.so.20.2.1
7f8dbae000-7f8dbbd000 ---p 000a5000 b3:01 918396                         /lib/aarch64-linux-gnu/libgcrypt.so.20.2.1
7f8dbbd000-7f8dbbf000 r--p 000a4000 b3:01 918396                         /lib/aarch64-linux-gnu/libgcrypt.so.20.2.1
7f8dbbf000-7f8dbc4000 rw-p 000a6000 b3:01 918396                         /lib/aarch64-linux-gnu/libgcrypt.so.20.2.1
7f8dbc4000-7f8dbc8000 r-xp 00000000 b3:01 918411                         /lib/aarch64-linux-gnu/libcap.so.2.25
7f8dbc8000-7f8dbd8000 ---p 00004000 b3:01 918411                         /lib/aarch64-linux-gnu/libcap.so.2.25
7f8dbd8000-7f8dbd9000 r--p 00004000 b3:01 918411                         /lib/aarch64-linux-gnu/libcap.so.2.25
7f8dbd9000-7f8dbda000 rw-p 00005000 b3:01 918411                         /lib/aarch64-linux-gnu/libcap.so.2.25
7f8dbda000-7f8dc1a000 r-xp 00000000 b3:01 918423                         /lib/aarch64-linux-gnu/libcryptsetup.so.12.2.0
7f8dc1a000-7f8dc2a000 ---p 00040000 b3:01 918423                         /lib/aarch64-linux-gnu/libcryptsetup.so.12.2.0
7f8dc2a000-7f8dc2b000 r--p 00040000 b3:01 918423                         /lib/aarch64-linux-gnu/libcryptsetup.so.12.2.0
7f8dc2b000-7f8dc2d000 rw-p 00041000 b3:01 918423                         /lib/aarch64-linux-gnu/libcryptsetup.so.12.2.0
7f8dc2d000-7f8dc34000 r-xp 00000000 b3:01 918385                         /lib/aarch64-linux-gnu/libacl.so.1.1.0
7f8dc34000-7f8dc43000 ---p 00007000 b3:01 918385                         /lib/aarch64-linux-gnu/libacl.so.1.1.0
7f8dc43000-7f8dc44000 r--p 00006000 b3:01 918385                         /lib/aarch64-linux-gnu/libacl.so.1.1.0
7f8dc44000-7f8dc45000 rw-p 00007000 b3:01 918385                         /lib/aarch64-linux-gnu/libacl.so.1.1.0
7f8dc45000-7f8dc53000 r-xp 00000000 b3:01 918328                         /lib/aarch64-linux-gnu/libapparmor.so.1.4.2
7f8dc53000-7f8dc62000 ---p 0000e000 b3:01 918328                         /lib/aarch64-linux-gnu/libapparmor.so.1.4.2
7f8dc62000-7f8dc63000 r--p 0000d000 b3:01 918328                         /lib/aarch64-linux-gnu/libapparmor.so.1.4.2
7f8dc63000-7f8dc64000 rw-p 0000e000 b3:01 918328                         /lib/aarch64-linux-gnu/libapparmor.so.1.4.2
7f8dc64000-7f8dc76000 r-xp 00000000 b3:01 918336                         /lib/aarch64-linux-gnu/libkmod.so.2.3.2
7f8dc76000-7f8dc86000 ---p 00012000 b3:01 918336                         /lib/aarch64-linux-gnu/libkmod.so.2.3.2
7f8dc86000-7f8dc87000 r--p 00012000 b3:01 918336                         /lib/aarch64-linux-gnu/libkmod.so.2.3.2
7f8dc87000-7f8dc88000 rw-p 00013000 b3:01 918336                         /lib/aarch64-linux-gnu/libkmod.so.2.3.2
7f8dc88000-7f8dca4000 r-xp 00000000 b3:01 918415                         /lib/aarch64-linux-gnu/libaudit.so.1.0.0
7f8dca4000-7f8dcb3000 ---p 0001c000 b3:01 918415                         /lib/aarch64-linux-gnu/libaudit.so.1.0.0
7f8dcb3000-7f8dcb4000 r--p 0001b000 b3:01 918415                         /lib/aarch64-linux-gnu/libaudit.so.1.0.0
7f8dcb4000-7f8dcb5000 rw-p 0001c000 b3:01 918415                         /lib/aarch64-linux-gnu/libaudit.so.1.0.0
7f8dcb5000-7f8dcbf000 rw-p 00000000 00:00 0 
7f8dcbf000-7f8dccb000 r-xp 00000000 b3:01 918319                         /lib/aarch64-linux-gnu/libpam.so.0.83.1
7f8dccb000-7f8dcda000 ---p 0000c000 b3:01 918319                         /lib/aarch64-linux-gnu/libpam.so.0.83.1
7f8dcda000-7f8dcdb000 r--p 0000b000 b3:01 918319                         /lib/aarch64-linux-gnu/libpam.so.0.83.1
7f8dcdb000-7f8dcdc000 rw-p 0000c000 b3:01 918319                         /lib/aarch64-linux-gnu/libpam.so.0.83.1
7f8dcdc000-7f8dd1c000 r-xp 00000000 b3:01 918401                         /lib/aarch64-linux-gnu/libblkid.so.1.1.0
7f8dd1c000-7f8dd2c000 ---p 00040000 b3:01 918401                         /lib/aarch64-linux-gnu/libblkid.so.1.1.0
7f8dd2c000-7f8dd30000 r--p 00040000 b3:01 918401                         /lib/aarch64-linux-gnu/libblkid.so.1.1.0
7f8dd30000-7f8dd31000 rw-p 00044000 b3:01 918401                         /lib/aarch64-linux-gnu/libblkid.so.1.1.0
7f8dd31000-7f8dd32000 rw-p 00000000 00:00 0 
7f8dd32000-7f8dd7c000 r-xp 00000000 b3:01 918350                         /lib/aarch64-linux-gnu/libmount.so.1.1.0
7f8dd7c000-7f8dd8b000 ---p 0004a000 b3:01 918350                         /lib/aarch64-linux-gnu/libmount.so.1.1.0
7f8dd8b000-7f8dd8d000 r--p 00049000 b3:01 918350                         /lib/aarch64-linux-gnu/libmount.so.1.1.0
7f8dd8d000-7f8dd8e000 rw-p 0004b000 b3:01 918350                         /lib/aarch64-linux-gnu/libmount.so.1.1.0
7f8dd8e000-7f8dd8f000 rw-p 00000000 00:00 0 
7f8dd8f000-7f8ddaf000 r-xp 00000000 b3:01 918257                         /lib/aarch64-linux-gnu/libselinux.so.1
7f8ddaf000-7f8ddbe000 ---p 00020000 b3:01 918257                         /lib/aarch64-linux-gnu/libselinux.so.1
7f8ddbe000-7f8ddbf000 r--p 0001f000 b3:01 918257                         /lib/aarch64-linux-gnu/libselinux.so.1
7f8ddbf000-7f8ddc0000 rw-p 00020000 b3:01 918257                         /lib/aarch64-linux-gnu/libselinux.so.1
7f8ddc0000-7f8ddc2000 rw-p 00000000 00:00 0 
7f8ddc2000-7f8ddf3000 r-xp 00000000 b3:01 918349                         /lib/aarch64-linux-gnu/libseccomp.so.2.4.3
7f8ddf3000-7f8de02000 ---p 00031000 b3:01 918349                         /lib/aarch64-linux-gnu/libseccomp.so.2.4.3
7f8de02000-7f8de1b000 r--p 00030000 b3:01 918349                         /lib/aarch64-linux-gnu/libseccomp.so.2.4.3
7f8de1b000-7f8de1c000 rw-p 00049000 b3:01 918349                         /lib/aarch64-linux-gnu/libseccomp.so.2.4.3
7f8de1c000-7f8de22000 r-xp 00000000 b3:01 918326                         /lib/aarch64-linux-gnu/librt-2.27.so
7f8de22000-7f8de31000 ---p 00006000 b3:01 918326                         /lib/aarch64-linux-gnu/librt-2.27.so
7f8de31000-7f8de32000 r--p 00005000 b3:01 918326                         /lib/aarch64-linux-gnu/librt-2.27.so
7f8de32000-7f8de33000 rw-p 00006000 b3:01 918326                         /lib/aarch64-linux-gnu/librt-2.27.so
7f8de33000-7f8dfb6000 r-xp 00000000 b3:01 917552                         /lib/systemd/libsystemd-shared-237.so
7f8dfb6000-7f8dfc6000 ---p 00183000 b3:01 917552                         /lib/systemd/libsystemd-shared-237.so
7f8dfc6000-7f8e050000 r--p 00183000 b3:01 917552                         /lib/systemd/libsystemd-shared-237.so
7f8e050000-7f8e052000 rw-p 0020d000 b3:01 917552                         /lib/systemd/libsystemd-shared-237.so
7f8e052000-7f8e054000 rw-p 00000000 00:00 0 
7f8e054000-7f8e193000 r-xp 00000000 b3:01 918299                         /lib/aarch64-linux-gnu/libc-2.27.so
7f8e193000-7f8e1a3000 ---p 0013f000 b3:01 918299                         /lib/aarch64-linux-gnu/libc-2.27.so
7f8e1a3000-7f8e1a7000 r--p 0013f000 b3:01 918299                         /lib/aarch64-linux-gnu/libc-2.27.so
7f8e1a7000-7f8e1a9000 rw-p 00143000 b3:01 918299                         /lib/aarch64-linux-gnu/libc-2.27.so
7f8e1a9000-7f8e1ad000 rw-p 00000000 00:00 0 
7f8e1b3000-7f8e1d4000 rw-p 00000000 00:00 0 
7f8e1d4000-7f8e1f1000 r-xp 00000000 b3:01 918295                         /lib/aarch64-linux-gnu/ld-2.27.so
7f8e1f1000-7f8e1ff000 rw-p 00000000 00:00 0 
7f8e1ff000-7f8e200000 r--p 00000000 00:00 0                              [vvar]
7f8e200000-7f8e201000 r-xp 00000000 00:00 0                              [vdso]
7f8e201000-7f8e202000 r--p 0001d000 b3:01 918295                         /lib/aarch64-linux-gnu/ld-2.27.so
7f8e202000-7f8e204000 rw-p 0001e000 b3:01 918295                         /lib/aarch64-linux-gnu/ld-2.27.so
7ffd202000-7ffd223000 rw-p 00000000 00:00 0                              [stack]

由此可以看到进程的虚拟内存空间会被分成不同的若干区域,每个区域都有其相关的属性和用途,一个合法的地址总是落在某个区域当中的,这些区域也不会重叠。在linux内核中,这样的区域被称之为虚拟内存区域(virtual memory areas),简称 VMA。一个vma就是一块连续的线性地址空间的抽象,它拥有自身的权限(可读,可写,可执行等等) ,每一个虚拟内存区域都由一个相关的 struct vm_area_struct 结构来描述。

从进程的角度来讲,VMA 其实是虚拟空间的内存块,一个进程的内存资源由多个内存块组成,所以,一个进程的描述结构 task_struct 中首先包含Linux的内存描述符 struct mm_struct 结构。

struct task_struct {
.......
  struct mm_struct *mm;
.......
};

在 mm_struct 中进而包含了 vm_area_struct :

struct mm_struct {
.......
  struct maple_tree mm_mt;
.......

}

一个进程的每个 VMA 块都会链接到mm_struct中的maple_tree。maple_tree是一颗什么样的树,我没有精力分析。内核文档说,它是一颗b数,用于存储非重叠的数。

接下来看看这次的主角 struct vm_area_struct:

struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. */

	unsigned long vm_start;		/* Our start address within vm_mm. */
	unsigned long vm_end;		/* The first byte after our end address
					   within vm_mm. */

	struct mm_struct *vm_mm;	/* The address space we belong to. */

	/*
	 * Access permissions of this VMA.
	 * See vmf_insert_mixed_prot() for discussion.
	 */
	pgprot_t vm_page_prot;  // 此VMA的访问权限
	unsigned long vm_flags;		/* Flags, see mm.h. */

	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 * 对于具有地址空间(address apace)和后备存储(backing store)的区域,
	 * 链接到address_space->i_mmap间隔树,或者链接到address_space-> i_mmap_nonlinear列表中的vma。
	 *
	 */
	struct {
		struct rb_node rb;
		unsigned long rb_subtree_last;
	} shared;

	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
	 * or brk vma (with NULL file) can only be in an anon_vma list.
	 * 
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
					  * page_table_lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops; //  用于处理此结构体的函数指针

	/* Information about our backing store: */ // 所谓的后背存储就是指该内存映射的文件
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */

#ifdef CONFIG_SWAP
	atomic_long_t swap_readahead_info;
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout

挑几个关键成员分析:

anon_vma

struct anon_vma_chain {
	struct vm_area_struct *vma;
	struct anon_vma *anon_vma;
	struct list_head same_vma;   /* locked by mmap_lock & page_table_lock */ 
	struct rb_node rb;			/* locked by anon_vma->rwsem */
	unsigned long rb_subtree_last;
};

anon_vma,简单说,链接物理page和vma的桥梁,简称av;

其关系图:

linux源码解析13- 反向映射RAMP详解

反向映射的引入

所谓反向映射是相对于从虚拟地址到物理地址的映射,反向映射是从物理页面到虚拟地址空间VMA的反向映射。反向映射通常记为rmap。RMAP能否实现的基础是通过struct anon_vma、struct anon_vma_chain和sturct vm_area_struct建立了联系,通过物理页面反向查找到VMA。

反向映射的需求来源:

当Linux系统内存不足时, swap子系统会释放一些页面, 交换到交换设备中, 以空出多余的内存页。虚拟内存的理念就是通过页表来维护虚拟地址到物理地址的映射。但是, 页表是种单向映射, 即通过虚拟地址查找物理地址很容易, 但反之通过物理地址查找虚拟地址则很麻烦。

同时,由于fork的 copy-on-write机制,导致一个物理page可能被多个进程的vma同时映射。

当Linux系统内存不足时, swap子系统会释放一些页面, 交换到交换设备中, 以空出多余的内存页。虚拟内存的理念就是通过页表来维护虚拟地址到物理地址的映射。但是, 页表是种单向映射, 即通过虚拟地址查找物理地址很容易, 但反之通过物理地址查找虚拟地址则很麻烦。这种问题在共享内存的情况下更加严重。而swap子系统在释放页面时就遇到这个问题, 对于特定页面(物理地址), 要找到映射到它的页表项(PTE), 并修改PTE, 以使其指向交换设备中的该页的位置。在2.4之前的内核中, 这是件费时的工作, 因为内核需要遍历每一个进程的所有页表, 以找出所有映射该页的页表项。

解决这一问题的做法是引入**反向映射(reverse mapping)**这一概念。该做法就是为每一个内存页(struct page)维护一个数据结构, 其中包含所有映射到该页的PTE, 这样在寻找一个内存页的反向映射时只要扫描这个结构即可, 大大提高了效率。这正是Rik van Riel的做法, 他在struct page中增加了一个pte_chain的字段, 它是一个指向所有映射到该页的PTE的链表指针。

当然, 它是有代价的。

  • 每个struct page都增加了一个字段, 而系统中每个内存页都对应一个struct page结构, 这意味着相当数量的内存被用来维护这个字段。而struct page是重要的内核数据结构, 存放在有限的低端内存中, 增加一个字段浪费了大量的保贵低端内存, 而且, 当物理内存很大时, 这种情况更突出, 这引起了**伸缩性(scalability)**问题。
  • 其它一些需要操作大量页面的函数慢下来了。fork()系统调用就是一个。由于Linux采取**写时复制(COW, Copy On Write)**的语义, 意味着新进程共享父进程的页表, 这样, 进程地址空间内的所有页都新增了一个PTE指向它, 因此, 需要为每个页新增一个反向映射, 这显著地拖慢了速度。

基于对象的反向映射

这种代价显然是不能容忍的, 于是, Dave McCracken提出了一个叫做**基于对象的反向映射(object-based reverse mapping)**的解决方案。他的观察是, 前面所述的代价来源于反向映射字段的引入, 而如果存在可以从struct page中获取映射到该页面的所有页表项, 这个字段就不需要了, 自然不需要付出这些代价。他确实找到了一种方法。

Linux的用户态内存页大致分两种使用情况:

  • 其中一大部分叫做文件后备页(file-backed page), 顾名思义, 这种内存页的内容关联着后备存储系统中的文件, 比如程序的代码, 比如普通的文本文件, 这种内存页使用时一般通过上述的mmap系统调用映射到地址空间中, 并且, 在内存紧张时, 可以简单地丢弃, 因为可以从后备文件中轻易的恢复。
  • 一种叫匿名页(anonymous page), 这是一种普通的内存页, 比如栈或堆内存就属于这种, 这种内存页没有后备文件, 这也是其称为匿名的缘故。

Dave的方案中的对象指的就是第一种内存页的后备文件。他通过后备文件对象, 以迂回的方式算出PTE,在本文中就不做过多的介绍。

匿名页的反向映射

Dave的方案只解决了第一种内存页的反向映射, 于是, Andrea Arcangeli顺着Dave的思路, 给出了匿名页的反向映射解决方案。

如前所述, 匿名页没有所谓的后备文件, 但是, 匿名页有个特点, 就是它们都是私有的, 而非共享的(比如栈, 椎内存都是独立每个进程的, 非共享的)。这意味着, 每一个匿名内存页, 只有一个PTE关联着它, 也就是只有一个vma关联着它。Andrea的方案是复用struct pagemapping字段, 因为对于匿名页, mappingnull, 不指向后备空间。复用方法是利用C语言的union, 在匿名页的情况下,mapping字段不是指向struct address_space的指针, 而是指向关联该内存页的唯一的vma。由此, 也可以方便地计算出PTE来。

但是, 事情并不是如此简单。当进程被fork复制时, 前面已经说过, 由于COW的语义, 新进程只是复制父进程的页表, 这意味着现在一个匿名页有两个页表指向它了, 这样, 上面的简单复用mapping字段的做法不适用了, 因为一个指针, 如何表示两个vma呢。

Andrea的做法就是多加一层。新创建一个struct anon_vma结构, 现在mapping字段是指向它了, 而anon_vma中, 不出意料的, 包含一个链表, 链接起所有的vma。每当进程fork一个子进程, 子进程由于COW机制会复制父进程的vma, 这个新vma就链接到父进程中的anon_vma中。这样, 每次unmap一个内存页时, 通过mapping字段指向的anon_vma, 就可以找到可能关联该页的vma链表, 遍历该链表, 就可以找到所有映射到该匿名页的PTE。

img

这也有代价, 那就是

  • 每个struct vm_area_struct结构多了一个list_head结构字段用以串起所有的vma
  • 需要额外为anon_vma结构分配内存。

但是, 这种方案所需要的内存远小于前面所提的在每个struct page中增加一个反向映射字段来得少, 因此是可以接受的。

以上, 便介绍完了anon_vma结构的来由和作用。

anon_vma_chain

anon_vma结构的提出, 完善了反向映射机制, 一路看来, 无论是效率还是内存使用, 都有了提升, 应该说是很完美的一套解决方案。但现实不断提出难题。一开始提到的Rik van Riel就举了一种工作负载(workload)的例子来反驳说该方案有缺陷。

前面的匿名页反向映射机制在解除一页映射时, 通过访问anon_vma访问vma链表, 遍历整个vma链表, 以查找可能映射到该页的PTE。但是, 这种方法忽略了一点: 当进程fork而复制产生的子进程中的vma如果发生了写访问, 将会分配新的匿名页, 把该vma指向这个新的匿名页, 这个vma就跟原来的那个匿名页没有关系了, 但原来的vma链表却没反映出这种变化, 从而导致了对该vma不必要的检查。 Rik举的例子正是对这种极端情况的描述。

Rik采取的方案是又增加一层, 新增了一个结构叫anon_vma_chain:

/*
 * The copy-on-write semantics of fork mean that an anon_vma
 * can become associated with multiple processes. Furthermore,
 * each child process will have its own anon_vma, where new
 * pages for that process are instantiated.
 *
 * This structure allows us to find the anon_vmas associated
 * with a VMA, or the VMAs associated with an anon_vma.
 * The "same_vma" list contains the anon_vma_chains linking
 * all the anon_vmas associated with this VMA.
 * The "same_anon_vma" list contains the anon_vma_chains
 * which link all the VMAs associated with this anon_vma.
 */
struct anon_vma_chain {
	struct vm_area_struct *vma;
	struct anon_vma *anon_vma;
	struct list_head same_vma;   /* locked by mmap_sem & page_table_lock */
	struct list_head same_anon_vma;	/* locked by anon_vma->lock */
};

每个anon_vma_chain(AVC)维护两个链表

  • same_vma:与给定vma相关联的所有anon_vma
  • same_anon_vma:与给定anon_vma相关联的所有vma

最初,我们有一个进程与一个匿名vma
img

这里,“AV”是anon_vma,“AVC”是上面看到的anon_vma_chain。 AVC直接通过指针链接到anon_vmavma。 (蓝色)链表是same_anon_vma链表,而(红色)链表是same_vma链表。

想象一下,这个进程进行了fork操作,导致子进程复制了vma; 现在有了一个孤立的新vma
img

内核需要将此vma链接到父进程的anon_vma中; 这需要添加一个新的anon_vma_chain
img

请注意,新的AVC已被添加到same_anon_vma链表中。 新的vma也需要自己的anon_vma
img
现在还有另一个anon_vma_chain链接在新的anon_vma中。 新的AVC已被添加到same_vma链表中。

此刻,根据上图,可以验证anon_vma_chain(AVC)中两个链表的作用。

The “same_vma” list contains the anon_vma_chains linking all the anon_vmas associated with this VMA.
The “same_anon_vma” list contains the anon_vma_chains which link all the VMAs associated with this anon_vma.

当子进程写内存页时,发生COW, 子进程的vma将指向自己匿名页, 同时, 这个新的匿名页指向子进程的anon_vma(此时same_anon_vma链与same_vma链解除)。
img

这样, 在解除一页映射时, 对于子进程自己的匿名页, 只要遍历子进程自己的anon_vma下的vma链表即可; 拥有大量子进程的父进程对于共享的页(未发生COW), 则按原来的方法遍历, 对于子进程自己的匿名页,父进程则不需要访问对应的vma,这样大大减少了父进程需要遍历的vma

再看anon_vma_chain这个名字, 它就像个粘合剂, 也像个链条, 把初始时父,子进程关联的vmaanon_vma链接起来, 当子进程通过COW拥有自己的匿名页后, 会发生解链, 以分冶策略各自管理, 从而使得在解除一页映射时, 减少了父进程遍历的vma数目, 也减少了相应的锁冲突, 因而提高了效率。

vm_flags

权限

参考:
https://www.cnblogs.com/arnoldlu/p/8335483.html
https://blog.csdn.net/youzhangjing_/article/details/127640564
https://blog.csdn.net/qq_38654981/article/details/127061582

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/159907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

靶机测试ReconForce笔记

靶机地址https://www.vulnhub.com/entry/hacknos-reconforce,416/靶机测试信息收集nmap扫描端口nmap扫描结果└─$ nmap -sC -sV 192.168.1.100 -oA hack …

结合NWR,让Paxos拥有的动态的Quorum,以及在Klein中的实践

Paxos Quorum面临的困境在原生的Basic-Paxos或者Multi-Paxos中&#xff0c;Quorum的数量要求的是多数派&#xff0c;例如&#xff1a;一个5成员组成的Paxos集群&#xff0c;Prepare和Accept阶段需要获得3个Acceptor的支持。Quorum3的条件&#xff0c;在原生的Paxos中是硬性条件…

NTP服务器(GPS北斗授时服务器)的市场需求及性能分析

NTP服务器&#xff08;GPS北斗授时服务器&#xff09;的市场需求及性能分析 NTP服务器&#xff08;GPS北斗授时服务器&#xff09;的市场需求及性能分析 目前国内NTP时间服务器时间精度已达到毫秒级&#xff0c;能够满足各个行业对时间同步运转的要求&#xff1b;有些企业需要…

QtitanComponents Solution 2022.4.0 Crack

2022-12-29 开始开发基于Adobe XD文件的Qt样式创建工具 包括&#xff1a; QtitanDataGrid QtitanRibbon QtitanNavigationDesignUI QtitanChart QtitanDocking QtitanFastInfoset 不要问我源码&#xff0c;没有&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#x…

Spring Boot 加载外部配置文件

Spring Boot 允许你从外部加载配置&#xff0c;这样的话&#xff0c;就可以在不同的环境中使用相同的代码。支持的外部配置源包括&#xff1a;Java属性文件、YAML文件、环境变量、命令行参数。用Value注解可以将属性值直接注入到beans中。命令行参数以 -- 开头配置文件加载的顺…

数据结构学习

数据结构 单值二叉树 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 类似上述图中&#xff0c;所有结点都为1&#xff0c;那么返回true&#xff0c;只要有一个结…

QT智能停车管理系统

智能停车管理系统 密级Confidentiality level 需求说明书 内部公开 产品版本Product version Total pages 共 7页 V100R001 智能停车管理系统项目 需求规格说明书 拟制人&#xff1a; 孙献 时间&#xff1a; 2023.1.7 评审人&#xff1a; 覃丽红 时间&#xff1a;…

Java项目:网上水果超市商城设计和实现(java+SSM+springboot+redis)

源码获取&#xff1a;本博客首页 "资源" 处下载&#xff01; 主要技术实现&#xff1a;spring、 springmvc、 redis、 springboot、 mybatis 、session、 jquery 、 md5 、bootstarp.js tomcat、、拦截器等。 主要功能实现&#xff1a; 前端&#xff1a;登录、注册…

Instagram 账号被封如何申诉拿回账号?ins账号解封经验分享

不知道各位在玩转海外社媒平台时有没有遇到过Instagram账号异常的情况&#xff0c;比如会出现账号受限、帖子发不出去、账号被封号等情况? Instagram账号如果被封不用马上弃用&#xff0c;我们可以先尝试一下申诉&#xff0c;看看能不能把账号解封。所以今天东哥将会出一篇Ins…

94、【树与二叉树】leetcode ——110. 平衡二叉树(C++版本)

题目描述 原题链接&#xff1a;110. 平衡二叉树 解题思路 一、后序遍历&#xff08;自底向上&#xff09; 在这里要和 90、【树与二叉树】leetcode ——104. 二叉树的最大深度&#xff1a;层次遍历DFS子问题分解&#xff08;C版本&#xff09; 这个作比较。 深度&#xff0…

黑马前端毕业生面试攻略

为不断提升学生就业质量&#xff0c;黑马上海校区率先实施“新就业模式”&#xff0c;采用精细化管理&#xff0c;将就业动作拆解到了课程周期内的各个阶段&#xff0c;提前制定和铺垫课程阶段对应的就业动作目标和重点&#xff0c;并严格落实把控&#xff0c;更高效地完成学生…

JavaScript Event对象

文章目录JavaScript Event对象概述Event对象属性方法event对象常用属性typekeyCode获取Event对象兼容代码获取事件的目标对象target属性和currentTarget属性JavaScript Event对象 概述 事件在浏览器中是以Event对象的形式存在的&#xff0c;每触发一个事件&#xff0c;就会产…

【魅力开源】第2集:三个人每人一个想法会发生什么故事?关于Odoo的江湖

文章目录一. 关于权限控制&#xff1a;组织、角色、人员权限的说明二. 关于快速批量功能&#xff1a;有一些事情可以交给电脑来做三. 关于可以拖拉拽应用&#xff1a;技术模块3.1 可配置的MVC3.2 简单开发下附件模块也可用来作为企业的知识沉淀3.3 多语言支持&#xff1a;对于跨…

第五届“泰迪杯”数据分析技能赛 经验代码分享

第五届“泰迪杯”数据分析技能赛 经验/代码分享 品牌&#xff1a;“泰迪杯”数据分析技能赛 组织单位&#xff1a;泰迪杯数据分析技能赛组织委员会、广东泰迪智能科技股份有限公司、广东省工业与应用数学学会、人民邮电出版社、北京泰迪云智信息技术研究院、网宿科技股份有限…

内存管理详解

目录 一、C/C中内存分布 二、C语言的内存管理方式 三、C的内存管理方式 3.1 new/delete操作内置类型 3.2 new/delete操作自定义类型 3.3 operator new()和operator delete()函数 3.4 重载operator new()和operator delete() 四、new、delete的实现原理 4.1 内置类型 …

(02)Cartographer源码无死角解析-(50) 2D点云扫描匹配→相关性暴力匹配2:RealTimeCorrelativeScanMatcher2D

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文末…

【论文速递】TPAMI2022 - 小样本分割的整体原型激活

【论文速递】TPAMI2022 - 小样本分割的整体原型激活 【论文原文】&#xff1a;Holistic Prototype Activation for Few-Shot Segmentation 获取地址&#xff1a;https://ieeexplore.ieee.org/document/9839487 CSDN下载&#xff1a;https://download.csdn.net/download/qq_36…

三十、RabbitMQ(1)

&#x1f33b;&#x1f33b; 目录一、 关于中间件的概述二、基于消息中间件的分布式系统的架构2.1 消息中间件应用的场景2.2 常见的消息中间件2.3 消息中间件的本质及设计2.4 消息中间件的核心组成部分2.5 小总结三、消息队列协议3.1 什么是协议3.2 网络协议的三要素3.3 AMQP 协…

JAVA 23种设计模式示例

目录 一.单例模式 二.工厂方法模式 三.抽象工厂模式 四.建造者模式 五.原型模式 六.享元模式 七.门面模式 八.适配器模式 九.装饰者模式 十.策略模式 十一.模板方法模式 十二.观察者模式 十三.责任链模式 十四.代理模式 十五.桥接模式 十六.组合模式 十七.命令…

openGauss数据库PostGIS 安装与使用

目录 概述 1.PostGIS 安装 1.1 GCC-7.3编译器安装 1.2PostGIS依赖库安装 1.3.安装Postgis 2.使用Extension 2.1创建PostGIS Extension 2.2使用Extension 2.3删除Extension 概述 PostGIS Extension是PostgreSQL的空间数据库扩展&#xff0c;提供如下空间信息服务功能&…