一、概述
内核区和与用户区的进程地址空间
Linux把进程地址空间分成内核区和用户区两部分。
- 操作系统内核的代码和数据等被映射到内核区
- 进程可执行映像(代码和数据)映射到虚拟内存的用户区
注:此地址空间为操作系统原理所定义的逻辑地址空间。用户区最大空间为0到3G,内核区为3G以上的空间。
内核态和用户态分配内存的方式不同
- 内核态可直接获得获得动态内存。
- 用户态请求动态内存时,并没有立即获得实际的物理页框(又称物理块),而仅仅获得对一个新的线性地址区间的使用权 这个线性地址区间会成为进程地址空间的一部分,称作线性区,每个线性区由起始线性地址、结束地址和一些存取权限描述组成。进程的地址空间是由允许该进程访问的全部线性地址组成。
下图为用户进程空间描述图。
进程描述符task_struct 中有mm字段,它是指向内存描述符mm_struct 结构体的指针。线性区即虚拟内存区域由vm_area_struct结构体描述,线性区的开始和结束都必须4KB对齐 ,进程只能访问某个有效的线性区 如果进程试图访问一个有效的area之外的地址或者用不正确的方式访问一个有效的area,内核将通过段异常(segmentation fault)杀死这个进程。内存描述符的mmap字段来查找线性区, mmap字段指向链表中的第一个线性区描述符。mmap_cache用来缓存最近用过的线性区。
内存描述符mm_struct
- mm_users:共享mm_struct的轻量级进程的个数
- mm_count:如果把内核描述符暂时借给一个内核线程,则增加mm_count。内存描述符的主使用计数器 mm_users次使用计数器的所有用户在mm_count中只作为一个单位 。每当mm_users减少时,内核都要检查它是否变为0,若是,则解除这个内存描述符。
进程获得新线性区的情况
- 刚刚创建的新进程
- 使用exec系统调用装载一个新的程序运行
- 将一个文件(或部分)映射到进程地址空间中
- 当用户堆栈不够用的时候,扩展堆栈对应的线性区
线性区的链表和红黑树结构
- mmap指向的线性区链表用来遍历整个进程的地址空间
- 红黑树mm_rb用来定位一个给定的线性地址落在进程地址空间中的哪一个线性区中
缺页异常
内核只是通过mmap()等调用分配了一些线性地址空间给进程,并没有真正的把实际的物理页框分配给进程 ,当进程试图访问这些分配给它的地址空间时,并没有物理页框对应于这些线性地址,就会引发一个缺页异常。
进程访问的线性地址不处于用户空间的线性区中,这种情况得判断是不是因为用户进程的栈空间消耗完而触发的缺页异常,如果是的话则在用户空间对栈区域进行扩展,并且分配相应的物理页,如果不是则作为一次非法地址访问来处理,内核将终结进程。
注:图中非连续内存地址是指3G以上的地址空间;堆栈由于入栈操作,导致区域不固定;在对内核空间访问为否的情况下,即在对用户空间的访问,此时若出现中断、软终端、临界区等情况只能说明地址错误,杀死此进程。
写时复制
子进程复制父进程的整个地址空间非常耗时,并且这样做经常毫无意义。父进程和子进程共享页框而不是复制页框。父进程和子进程何时试图写一个共享的页框,就产生一个异常,这时内核就把这个页复制到一个新的页框中并标记为可写。