用户态与内核态的划分
- 进程的虚拟地址空间,就是换了个角度看内存。
- 整个虚拟内存要一分为二, 一部分是内存态地址空间,一部分是内核态的内存空间
- 32位系统,最大寻址是2^32 = 4G,其中用户态虚拟地址空间是3G, 内核态是1G。
- 64位系统,虚拟地址只使用了 48位, 1 >> 47 相当于 47 ^2 ,也就是内核态和用户态各自拥有128T.
用户态的布局
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位
64位