地址空间
Linux操作系统采用虚拟内存技术,因此系统中的所有进程之间以虚拟方式共享内存。对一个进程而言,它可以访问整个系统的所有物理内存,并且它拥有的地址空间也可以远远大于系统物理内存。尽管一个进程可以寻址4GB的虚拟内存(以32位的系统为例),但这并不代表它有权访问所有的虚拟地址,进程只能访问有效内存区域内的内存地址。每个内存区域也具有相关权限控制(可读,可写,可执行),如果一个进程访问了非有效内存区域内的地址,或者以不正确的方式访问了有效地址,那么内核就会终止该进程并返回“段错误”信息。内存区域主要包括:代码段,数据段,BSS段,用户空间栈,内存映射文件,共享内存段,匿名的内存映射(如由malloc分配的内存),共享库的代码段,数据段和BSS段。
内存描述符
内核使用内存描述符来表示进程的地址空间,结构体为mm_struct。mm_user域记录正在使用该地址的进程数目(通常用于线程之间共享进程地址空间)。mm_count域是mm_struct结构体的主引用计数。mmap和mm_rb两个不同的域描述的对象是相同的,即该地址空间中的全部内存区域。但是前者以链表形式存放,后者以红黑树形式存放,所有的mm_struct结构体都通过自身的mmlist域连接在一个双向链表中。进程描述符task_struct结构体中的mm域存放着该进程的内存描述符。fork()函数利用copy_mm()函数复制父进程的内存描述符,而子进程的mm_struct结构体实际是通过allocate_mm()宏从mm_cachep_slab缓存中分配得到的。当进程退出时,内核会调用exit_mm()函数,该函数执行一些常规的撤销工作,同时更新一些统计量。其中,该函数会调用mmput()函数减少内存描述符中的mm_users用户计数,如果计数降到零,调用mmdrop()函数,减少mm_count使用计数。如果mm_count也等于零,则调用free_mm()宏通过kmem_cache_free函数将mm_struct结构体归还到mm_cachep_slab缓存中。内核线程没有进程地址空间,也没有相关的内存描述符,其task_struct结构体中的mm域为空。事实上,这也正是内核线程的含义——内核线程没有用户上下文。当一个进程被调度时,该进程的mm域指向的地址空间被装载到内存,进程描述符中的active_mm域被更新,指向新的地址空间。内核线程由于其mm域为NULL,所以会保留前一个进程的地址空间,并将内核线程的active_mm域指向前一个进程的内存描述符。所以在需要时,内核线程便可以使用前一个进程的页表。内核线程不使用用户空间的内存,仅仅访问地址空间中和内核内存相关的信息。
虚拟内存区域
虚拟内存区域由vm_area_struct结构体描述,其表示指定地址空间内连续区间上的一个独立内存范围。内核将每个内存区域作为一个单独的内存对象管理,每个内存区域拥有一支的属性,例如访问权限等。每个内存描述符都对应于进程地址空间中的唯一区间。vm_start域指向区间的首地址,vm_end域指向区间的尾地址之后的一个字节。vm_mm域指向和VMA相关的mm_struct结构体。每个VMA对其相关的mm_struct结构体来说都是唯一的。