本篇为基础,讲解内存管理部分的基本原理与进程虚拟空间布局
文章目录
1.独占空间的原理
对于每一个进程都应该有自己看起来独占的内存空间,以实现不同进程之间的隔离,保证安全性。而之所以提出这种巧妙的机制是为了解决无法直接使用物理内存地址的问题。因为考虑到机器多为多进程多任务的运行机制,当多个进程若同时使用相同的物理内存地址,此时显然会产生错误的结果。故需要设计使用虚拟地址,建立虚拟地址与物理地址之间的映射关系,然后使用时由内核数据结构负责为虚拟地址找到合适的物理内存将数据写入进去。
2.虚拟内存的划分方式
32bit机器会有232=4G的虚拟空间,64bit机器(只使用48bit寻址)会有248=256TB的虚拟空间,将这些虚拟空间会按照某种方式划分为两部分,一部分用来存放内核东西叫做内核空间,另一部分用于存放当前进程的东西叫做用户空间,具体划分方式以后讨论。
用户空间的内容划分: 从下到上依次是代码段,数据段(放静态常量),附加段(放未初始化的静态变量),堆,内存映射取,栈。用户若需要访问内核空间就需要调用系统调用陷入内核。和用户空间不同的是,在用户空间每个进程都要自己的用户空间,但进入内核之后却是同一个内核空间,不过每个内核空间都有自己的栈空间,所以在内核里面若要访问公共资源才需要使用锁保护。
- 注意:可以使用
pmap $(pid)
或者cat /proc/$(pid)/maps
查看Linux系统上某一个进程(pid)的内存空间布局
3.Linux虚拟地址与物理地址的转换
需要注意的是,虽然在地址转换上有分段机制这种操作可以实现地址转换,但Linux并不依靠分段机制,它仅仅只是利用段选择子中的特权标记来区分访问权限。Linux更倾向于使用页表机制来进行虚实地址的转换。
- 32bit的Linux虚拟地址的构成(两级目录)
页目录项 | 页表项 | 页内偏移 |
---|---|---|
10bits | 10bits | 12bits |
- 64bit的Linux虚拟地址的构成(四级目录)
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
全局页目录项PGD | 上层页目录项PUD | 中间页目录项PMD | 页表项PTE | 页内偏移地址 |
4.从mm_struct结构开始讨论进程虚拟空间布局
在每个进程的task_struct结构体中有一个成员变量mm_struct用来管理内存:
struct mm_struct *mm;
该结构体里面有许多成员变量,如其中一个成员变量task_size就是用来划分虚拟内存的用户空间与内核空间的变量。系统空间布局如下图所示:
(1)用户态空间的布局
下面来看一看和用户态相关的部分mm_struct成员数据结构的内核定义:
//linux-4.13.16\include\linux\mm_types.h
struct mm_struct {
struct vm_area_struct *mmap; /* 虚拟空间中各个分区构成链表的链表头 */
struct rb_root mm_rb;
...
unsigned long mmap_base; /*表示VA空间中用于内存映射的起始位置*/
...
unsigned long task_size; /* 用户空间与内核空间的分界线 */
...
unsigned long total_vm; /* 总共映射的页的数目 */
unsigned long locked_vm; /* 不能被系统调度换出内存的页面 */
unsigned long pinned_vm; /* 既不能被换出也不能被移到 */
unsigned long data_vm; /* 存放数据的页的数目 */
unsigned long exec_vm; /* 存放可执行文件的数目 */
unsigned long stack_vm;