以前初学C,java时,都被告知代码被分为什么段,什么段。
现在学了操作系统和编译原理之后。
突然就想到了, 程序被编译后分为数据段和代码段等其它段。 数据段存放数据,代码段存放编译之后的指令。
那么编译器将程序中定义的变量和数据放在数据段的相应位置,其地址都是逻辑地址。 即编译器编译的时候给了每个程序一个物理地址空间,里面的地址都是逻辑地址。
然后当程序运行时,就分配给程序相应地空间和PCB形成一个进程,当进行需要某个变量时就通过对逻辑地址进行运算,具体怎么运算取决于内存的分配方式,然后取得该变量的物理地址,然后就能取得该变量存储的数据了。
那么问题来了, 动态分配内存怎么搞? 当程序运行时,如何给程序分配控件。
为什么对程序进行分段, 虽然感觉分段好像有好处, 但是好处是什么呢呢呢呢呢?
以Windows平台PE格式为例,数据段有两种:.data和.bss。其中.data是用作存放程序里的字面量以及常量的。bss是放未初始化和初始化为0的全局变量的;
PE装入内存之后,.data段的数据还是原样,.bss段原来不占内存,装入后才占了片内存空间;
堆空间是运行时调用系统函数分配的,系统将一片物理内存映射到进程的一片用户地址空间,这个地址是不确定的。
现在的x86系列CPU属于普林斯顿体系结构,代码和数据是混放在一起的,要是不做区分,一不小心程序就跑飞了。
都是分配在虚拟地址 物理地址 程序看不到 由操作系统通过MMC等进行映射处理 对应用程序透明
即编译器编译的时候给了每个程序一个物理地址空间,里面的地址都是逻辑地址。
这就说错了,编译器给的是offset,然后linker给出静态存储的地址。操作系统把它装入内存,并且,这个地址是进程虚拟地址。任何程序不可能得到物理地址,除非内核态的程序和操作系统自己。
动态内存分配的也是虚拟地址,程序运行时,这些地址被叫做heap(堆)地址。
实模式,是实际内存地址,
保护模式,是虚拟地址,一个进程拥有一块地址空间,,,因为操作系统不暴露段给用户用
所以32Bits保护模式,各个段都是在4G地址空间之内。
这样,用户的数据段,代码段,甚至堆栈段,都是操作系统安排的
编译器只需要把数据分段,代码分段等告诉操作系统
.操作系统自然就会把代码数据,装入相应的段中
程序中也就不操作段寄存器了,虽然线程局部存储利用了FS寄存器,
但是FS对应的段,不是用户程序设置的
而是操作系统线程调度程序安排的。
保护模式的flat 模式(4G),相当于dos的tiny 模式,生成.com程序(64K)
完全不用管,段的问题。
预设的段,就够用了。
实模式,段设置是通过段地址设置的;
每16字节,一个台阶
段可以在任何16字节对齐的位置。
保护模式flat 模式,每个程序自己拥有4G地址空间
操作系统设置段描述符,段描述符表,中断描述符表,门描述符表,任务描述符表,页目录,页表,等
数据结构。而4G 偏移量,是用户可以使用的地址空间。
用户所有的内存空间都在这4G之内
这种虚拟内存的分配,包括分配地址空间,以及进行页面调度。
其中 页面调度,是设置好一定的内存和磁盘空间,页目录表,页表以后,系统缺页中断,自动调度的。
程序运行时候,是有代码段,数据段,堆栈段,可能还有附加段,以及线程局部存储,专用段的
对应寄存器分别为CS,DS,SS,ES,FS(GS) 其中GS 没有使用,DS,ES 应该相等
不过操作系统,把它全部控制在4G之内。段相关数据结构,以及分页等结构,是不暴露给用户程序的.
编译器,可以安排常量数据在代码段
非常量数据在数据段
这是编译器可以安排的
至于数据段的其他段,最终会合并到一个数据段。
float 模式,用户程序,本质上只有一个数据段。虽然 PE格式 ,程序可以有很多section
你甚至可以认为,整个程序(单线程的情况下),只有一个4G段