进程的虚拟内存布局

来源:互联网 发布:intent 传递数据 编辑:程序博客网 时间:2024/06/02 15:44

      每一个用户态进程拥有独立的3GB用户态虚拟地址空间,共享1GB的内核空间,而这3GB的地址不可能都映射了物理内存。task_struct的mm成员就是用来描述这3GB的虚拟地址信息的。对于内核态进程,由于没有3GB的用户态虚拟地址空间,所以其mm结构为NULL,可是为什么要在task_struct中设置mm和active_mm两个mm_struct成员呢?这是由于内核线程没有用户态地址空间,所以他的mm设置为NULL,但是由于页目录的地址保存在mm结构中,从其他进程切换到这个内核态进程是,调度器可能需要切换页表,为此增加了一个active_mm,对于mm为NULL的内核线程,就借用其他进程的mm_struct,也就是说把它的active_mm指向其他进程的mm结构。当进行进程切换时,统一使用active_mm就可以了,但是其他进程不是有独立的页表吗?由于内核态线程只使用内核地址空间,因此这不会有问题。

用户态进程都有一个可执行文件,start_code,end_code分别指向该进程代码段起始地址和结束地址,start_data,end_data指向进程数据段的起始地址和结束地址,start_stack指向进程的堆栈起始地址,start_brk指向进程的堆(heap)的起始地址,arg_start,arg_end指向进程的命令行参数,env_start,env_end指向该进程的环境变量。这个进程的虚拟地址空间布局如图所示:

QQ截图未命名

      用户可以通过cat /proc/pid/maps文件看到进程的虚拟地址空间分配情况,当运行一个可执行文件时,可执行文件的装载器会根据这个布局加载可执行文件。用户态虚拟地址空间的顶端地址为3GB,这里空出一小片区域不映射,接下来是进程的环境变量,再接下来是命令行参数,这是装载器放置好的,再往下就是动态库,对于某些库,只有一份代码存在物理内存中,但是被映射到了好几个进程的虚拟地址空间中。之后,就是进程的堆栈,对于C程序来说,argc,argv还有main函数的返回地址压入堆栈后,就调用main()函数。堆栈从高地址向低地址增长。从低地址0x80000000开始,是代码段和数据段,再往上就是堆heap的起始地址,堆从低地址向高地址增长,我们知道malloc()这一类函数就是从堆中分配内存的。这里堆和栈都是动态变化的,他们一个从上往下增长,另一个从下往上增长。对于堆栈操作,当进程访问到一个没有被映射的虚拟地址时,缺页中断会分配物理页面并建立映射。类似地,对于堆操作,当分配到物理内存后,就映射到堆的虚拟地址空间上。由于进程对堆和栈的大小都有限制,因此缺页中断可以保证它们在虚拟地址空间上不会相交。


http://blog.csdn.net/liujun01203/article/details/5862888



0 0