进程地址空间

嵌入式学习Day5


1.什么是进程地址空间

进程地址空间就是内核给每个进程画出来的一张“虚拟内存地图”,进程看到的是自己独占的一大片连续地址,比如从低地址到高地址一整条空间;但实际上这些地址不一定真的连续放在物理内存里,很多甚至暂时还没分配物理页。中间靠 页表 和 MMU 做转换。

它解决了什么问题:


2.进程空间中有什么

高地址
+---------------------+
|        栈 stack      |  //向下增长,保存函数调用现场,局部变量,保存的寄存器等
+---------------------+
|   内存映射区 mmap     |  //动态链接库 libc.so、ld.so|mmap() 映射的文件|共享内存|大块内存分配
+---------------------+
|        堆 heap       |  //向上增长,用来做动态内存分配,比如 malloc/new 申请的内存通常来自堆。
+---------------------+
| .data / .bss         |   //bss 段:未初始化或初始化为 0 的全局/静态变量    data 段:已初始化的全局/静态变量  
+---------------------+
|   代码段 text        |   //放程序指令,也就是机器码。
+---------------------+
低地址

3.内存描述符

3.1 mm_struct

mm_struct是一个进程用户地址空间的总描述符,管整个进程的:

内核要表示一个进程,主要用 task_struct。而 task_struct 里面会有一个指针,指向这个进程的 mm_struct。

mm_struct 下面挂着一个个 vm_area_struct,vm_area_struct对应每一段连续的虚拟内存区域

mm_struct
   +-- vm_area_struct   代码段
   +-- vm_area_struct   数据段
   +-- vm_area_struct   堆
   +-- vm_area_struct   栈
   +-- vm_area_struct   某个共享库映射
   +-- vm_area_struct   某个 mmap 文件映射

mm_struct代表了页表上下文,它“拥有并代表”这一整套地址空间所使用的页表。页表是“具体映射”,而之前说的VMA是虚拟地址

3.1.1 VMA 管理相关相关字段

3.1.2 页表相关字段

pgd_t *pgd;

3.1.3 地址边界相关:代码段、数据段、堆、栈在哪里

3.1.4 引用计数相关


3.1.5 并发保护

3.1.6 内存统计相关


3.2 线程与进程创建时的区别

创建进程时调用fork(),利用copy_mm() 复制父进程的内存描述符 子进程中的 mm_struct 实际是通过 allocate_mm() 从 mm_cachep slab 缓存中分配得到的。 也就是说:

新 mm_struct 这个壳子,本身是内核分配出来的。

然后才会往里面填:


如果父进程希望和子进程共享地址空间,可以在调用 clone() 时设置 CLONE_VM 标志。我们把这样的“进程”称作线程。也就是说线程和进程共享一个mm_struct

if (clone_flags & CLONE_VM) {
    atomic_inc(&current->mm->mm_users);
    tsk->mm = current->mm;
}

当线程创建成功时就是把之前我说的字段,mm_users自增

总结: 在 Linux 中,进程的地址空间由 mm_struct 描述,并通过 task_struct 中的 mm 指针与进程关联。普通 fork 时,子进程通常会建立自己的 mm_struct;而在 clone() 指定 CLONE_VM 时,父子任务共享同一个 mm_struct,此时内核只需让子任务的 mm 指向父任务的 mm,并增加 mm_users 计数。这样的任务通常被称为线程。


3.2.1 为什么“每个线程有自己的栈”

既然共享同一个 mm_struct,那栈为什么还不是完全同一个? 答案是:


3.3 内核线程没有自己的用户地址空间,是怎么运行的

mm == NULL
active_mm != NULL
active_mm 通常借自上一个用户进程

内核线程(Kernel Thread)确实没有属于自己的用户地址空间(在 Linux 的进程描述符 task_struct 中,它们的 mm 指针为空 NULL)。 核心在于共享的内核地址空间以及一种非常巧妙的“借用”页表机制。

进程地址空间 配图 1