unsigned long mmap_base; /\* base of mmap area \*/
unsigned long total_vm; /\* total\_vm 是总共映射的页的数目 \*/
unsigned long locked_vm; /\* Pages that have PG\_mlocked set \*/
unsigned long pinned_vm; /\* Refcount permanently increased \*/
unsigned long data_vm; /\* VM\_WRITE & ~VM\_SHARED & ~VM\_STACK \*/
unsigned long exec_vm; /\* VM\_EXEC & ~VM\_WRITE & ~VM\_STACK \*/
unsigned long stack_vm; /\* VM\_STACK \*/
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
- total_vm 是总共映射的页的数目,内存吃紧的时候,locked_vm 就是被锁定不能换出,pinned_vm 是不能换出,也不能移动。
换出 : 把内存写到硬盘
- data_vm 是存放数据的页的数目,exec_vm 是存放可执行文件的页的数目,stack_vm 是栈所占的页的数目。
- start_code 和 end_code 表示可执行代码的开始和结束位置,start_data 和 end_data 表示已初始化数据的开始位置和结束位置。
- start_brk 是堆的起始位置,brk 是堆当前的结束位置。前面咱们讲过 malloc 申请一小块内存的话,就是通过改变 brk 位置实现的。
- start_stack 是栈的起始位置,栈的结束位置在寄存器的栈顶指针中。
- arg_start 和 arg_end 是参数列表的位置, env_start 和 env_end 是环境变量的位置。它们都位于
栈中最高地址的地方
。 mmap_base
表示虚拟地址空间中用于内存映射的起始地址。这个空间是从高地址到低地址
增长的。
- 这里用红黑树,就是为了快速查找一个内存区域,并在需要改变的时候,能够快速修改
- anoy 就是 anonymous,匿名的意思,映射到文件就需要有 vm_file 指定被映射的文件。
- 当 exec 运行一个二进制程序的时候,除了解析 ELF 的格式之外,另外一个重要的事情就是建立内存映射。
- 内存映射图 :
- 下面这俩种情况都会修改上面的映射关系
- 第一种情况是函数的调用,涉及函数栈的改变,主要是改变栈顶指针。
- 第二种情况是通过 malloc 申请一个堆内的空间,当然底层要么执行 brk,要么执行 mmap。关于内存映射的部分。
- brk (堆) : 堆是从低地址向高地址增长的, 会有新旧的brk堆顶地址, 需要比较,如果俩者相同的话,则说明在同一页,则修改堆顶地址就行,指向新的地址, 如果新的 < 旧的 则说明不在一页,至少需要释放一页。
-
- brk 判断是否需要分配新页, 并做对应操作; 需要分配新页时需要判断能否与其他 vm_area_struct 合并
内核态的布局
32 位
64位
小结
- 内存管理信息在 task_struct 的 mm_struct 中
- task_size 指定用户态虚拟地址大小
- 32 位系统:3G 用户态, 1G 内核态
- 64 位系统(只利用 48 bit 地址): 128T 用户态; 128T 内核态
- 用户态地址空间布局和管理
- mm_struct 中有映射页的统计信息(总页数, 锁定页数, 数据/代码/栈映射页数等)以及各区域地址
- 有 vm_area_struct 描述各个区域(代码/数据/栈等)的属性(包含起始/终止地址, 可做的操作等), 通过链表和红黑树管理
- 在 load_elf_bianry 时做 vm_area_struct 与各区域的映射, 并将 elf 映射到内存, 将依赖 so 添加到内存映射
- 在函数调用时会修改栈顶指针; malloc 分配内存时会修改对应的区域信息(调用 brk 堆; 或调用 mmap 内存映射)
- brk 判断是否需要分配新页, 并做对应操作; 需要分配新页时需要判断能否与其他 vm_area_struct 合并
- 内核地址空间布局和管理
- 所有进程看到的内核虚拟地址空间是同一个
- 32 位系统, 前 896MB 为直接映射区(虚拟地址 - 3G = 物理地址)
- 直接映射区也需要建立页表, 通过虚拟地址访问(除了内存管理模块)
- 直接映射区组成: 1MB 启动时占用; 然后是内核代码/全局变量/BSS等,即 内核 ELF文件内容; 进程 task_struct 即内核栈也在其中
- 896MB 也称为高端内存(指物理内存)
- 剩余虚拟空间组成: 8MB 空余; 内核动态映射空间(动态分配内存, 映射放在内核页表中); 持久内存映射(储存物理页信息); 固定内存映射; 临时内存映射(例如为进程映射文件时使用)
- 64 位系统: 8T 空余; 64T 直接映射区域; 32T(动态映射); 1T(物理页描述结构 struct page); 512MB(内核代码, 也采用直接映射)
32位