操作系统原理:进程地址空间

Linux进程虚拟存储

先回忆一下ELF文件的组织结构,可以看这篇文章:Linux 链接与ELF文件。程序执行后进程地址空间布局则和操作系统密切相关。在将应用程序加载到内存空间执行时,操作系统负责代码段与数据段的加载,并在内存中为这些段分配空间。Linux的进程地址空间大致如下:

操作系统原理:进程地址空间_第1张图片

Linux内核虚拟存储

内核虚拟存储器包含了内核的代码和数据结构。内核虚拟存储器的一些区域被映射到所有进程共享的内存页面,比如每个进程共享内核的代码和全局数据结构。Linux也将一组连续的虚拟页面(大小等于同种DRAM的总量)映射到相应的一组连续的物理页面。这使得内核可以遍历的访问物理存储的任何特定位置。内核虚拟地址存储器的其他区域包含了每个进程都不同的数据,如页表、内核在进程上下文执行时所使用的栈,以及记录虚拟地址空间当前组织的各种数据结构。
操作系统原理:进程地址空间_第2张图片

Linux缺页异常处理

当MMU翻译某个虚拟地址A触发缺页时,此异常会导致控制转移到内核缺页处理程序:

  1. 虚拟地址不合法。缺页处理程序搜索进程区域结构链表,把A和每个区域的vm_start和vm_end比较,不合法,触发段错误,kill进程。
  2. 访问不合法。主要看进程是否有读写或执行这个区域内页面的权限,不合法则触发保护异常,Linux也会报段错误,终止进程。
  3. 正常缺页。内核判断地址和访问都是合法的,那么选择牺牲一个页面,如果被修改过交换出去,换入新页面更新页表。

如下图:
操作系统原理:进程地址空间_第3张图片

Linux虚拟存储组织

通过下面的图可以一看一下Linux内核数据结构组织一个进程虚拟地址空间的方式。内核为系统中每个进程维护了一个单独的任务结构task_struct, 结构中包含了内核运行该进程的所有信息。
操作系统原理:进程地址空间_第4张图片

Linux进程地址空间

栈 (stack)

函数调用借助的就是栈。Linux中ulimit -s命令可查看和设置栈最大值,调高栈容量可能会增加内存开销和启动时间。

内存映射段 (mmmap)

内核将硬盘文件的内容直接映射到内存,借助Linux的mmap()系统调用。这里也用来映射ELF文件用到的动态链接库。

堆 (heap)

分配的堆内存是经过字节对齐的空间,以适合原子操作。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片。

堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用。

数据段 (bss + data)

主要用来放全局变量和静态局部变量,地址编译期确定。

代码段 (text)

放的函数指令,地址编译期确定。

更多有意思的细节可以看后面参考文献。

Reference

Computer Systems Organization
Xv6, a simple Unix-like teaching operating system
Linux虚拟地址空间布局

你可能感兴趣的:(OS/Linux)