我们知道内核管理物理内存,其实除了管理本身内存外,还必须管理用户空间中进程的内存,我们称这个内存为进程地址空间,也就是系统中每个用户空间进程所看到的内存。
传统的C语言编译出来的进程地址空间包含哪些对象呢?可以参照ELF文件格式对应看:
-->可执行文件代码的内存映射,称为代码段。【ELF中的代码段】
-->可执行文件的已初始化的全局变量的内存映射,成为数据段。【ELF中的数据段】
-->未初始化的全局变量,也就是bss段的零页。由于未初始化的变量没有对应的值,所以不需要放在可执行对象中,但是C标准规定了所有未初始化全局变量要被赋予特殊的值,所以内核需要将变量从可执行代码载入到内存中,然后把零页映射到该片内存上。【ELF中的bss段】
所以全局变量是否初始化影响该变量在可执行文件中的位置,初始化的全局变量在数据段,未初始化的全局变量在bss段且默认置0。
另外,静态变量也就是static变量,和全局变量在内存中的处理相同,即初始化的static变量放在数据段,未初始化的static变量放在bss段,static变量和全局变量的不同在于语言层面上的作用域不同。
-->用于进程用户空间栈的零页内存映射(不同于内核进程栈)。堆栈段是进程运行时使用,所以在可执行文件中不需要实际分配。【动态分配】
-->每一个诸如c库或者动态链接库等共享库的代码段,数据段和bss段也会被载入进程的地址空间。当然静态库会编译到可执行文件中,而动态的共享库需要运行时载入。【动态分配】
-->任何内存映射文件,任何共享内存段,任何匿名的内存映射,比如malloc分配的内存。【动态分配】
每个用户进程看到的内存或多或少都是上面的几部分,但是这些内存是怎么和CPU交互,需要内核做什么呢?
当内核将CPU使用权交给这个进程时,指向代码段的寄存器会被内核写入这个进程的代码段地址。
地址的分类:
逻辑地址:包含在机器语言中用来指定一个操作数或者一条指令的地址。每一个逻辑地址由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离。
所以,逻辑地址一般是表示在可执行文件中机器语言所使用的地址,语境一般在可执行文件的机器指令中。内核一般不使用逻辑地址这个概念。
线性地址(或叫虚拟地址):是否用于描述内核中的地址。
物理地址:用于内存芯片级内存单元寻址。
CPU中有一个用于将逻辑地址直接映射为物理地址的控制线路,它叫MMU。内存控制单元(MMU)通过一种分段单元的硬件电路把一个逻辑地址转换成线性地址,接着,通过一个分页单元的硬件电路将线性地址转换为一个物理地址。就是说,一个hello可执行文件的机器指令,在使用比如一个定义的全局变量时,可以不通过内核,结合当前进程页表,通过MMU将全局变量的逻辑地址转换为实际的物理地址。
原文:http://www.cnblogs.com/minihaohao/p/5175056.html