进程地址空间
操作系统在管理内存时,每个进程都有一个独立的进程地址空间,进程地址空间的地址为虚拟地址,对于32位操作系统,该虚拟地址空间为2^32=4GB。 进程在执行的时候,看到和使用的内存地址都是虚拟地址,而操作系统通过MMU部件将进程使用的虚拟地址转换为物理地址。
linux系统中进程的地址空间分布如下图所示,其中在32位系统中0-3GB为用户空间,3-4GB为内核空间:
ELF文件中的内容布局
ELF(Executable Linkable Format)文件是linux下的可执行文件。ELF文件的结构如下所示:
其中ELF头中存放各个段的起始地址和长度以及其他的信息,各个段中存放不同属性内容。其中c程序中的变量在ELF文件中的存储区域如下图所示:
.bss段的大小可以通过ELF头中的信息得到,但是这只是一个“大小”数据,告诉程序中的非初始化的全局和静态变量会共占用多少内存,在文件中并不会有它的空间,只有当可执行文件装载运行时,才会被分配内存(并且位于data段内存块之后),并且初始化为0.
当可执行文件被装载入内存之后,各个段装载进的虚拟存储区域情况如下图所示:
内存分页
在实际进行虚拟地址到物理地址的转换时,需要使用MMU,MMU是一个硬件电路,包含两个部件,一个是分段部件,将一个逻辑地址转换为一个线性地址,一个是分页部件,将线性地址转换为物理地址。
程序启动
(1)操作系统分配一个独立的进程地址空间,主要是在内存的内核区域中新建一个描述进程的结构体(linux中为task_struct),结构体中包含了进程的相关信息,比如进程运行状态,进程的寄存器,进程打开的资源,以及进程的内存管理结构(在linux中为mm_struct,进程的内存管理结构就描述了进程的虚拟地址空间的布局). 同时,为该进程创建一个页目录表。
(2)读取可执行文件头,建立可执行文件中各个段和进程虚拟地址空间中各个段之间的映射关系。当程序运行时需要将可执行文件中的内容载入内存来执行,比如在进程访问某全局变量时,该全局变量还没有被载入内存,此时需要知道该全局变量对应在可执行文件的什么位置。于是我们就需要知道进程中虚拟地址到可执行文件中位置的对应。
执行的时候会不断的发生缺页中断,发生缺页中断时会将实际的可执行文件中的内容载入到物理内存中,然后建立虚拟内存页和物理内存页的映射关系。
系统对进程的管理
在进程切换的时候,系统将原进程的相关信息保存到它对应的task_struct中;然后选择另一个进程,将task_struct中的信息装载到机器的寄存器中去,然后新的进程就按照它的task_struct来指导运行....
fork进程
此时,子进程的虚拟内存页和父进程的虚拟内存页使用相同的物理页。当发生子进程或者父进程要对内存进行写操作时,系统再为子进程的对应的虚拟页分配物理页,即copy-on-write机制。