进程地址空间分布和可执行文件分布

进程地址空间

操作系统在管理内存时,每个进程都有一个独立的进程地址空间,进程地址空间的地址为虚拟地址,对于32位操作系统,该虚拟地址空间为2^32=4GB。 进程在执行的时候,看到和使用的内存地址都是虚拟地址,而操作系统通过MMU部件将进程使用的虚拟地址转换为物理地址。

进程地址空间中分为各个不同的部分: 
(1)由于系统内核中有些代码、数据是所有进程所公用的,所以所有进程的进程地址空间中有一个专门的区域存放公共的内核代码和数据,该区域内的内容相同,且该虚拟内存映射到同一个物理内存区域。 
(2)进程在执行的时候,需要维护进程相关的数据结构,比如页表、task和mm结构、内核栈等,这些数据结构是进程独立的,各个进程之间可能不同。这些数据结构在进程虚拟地址空间中一个专门的区域中。 
(3)进程在进行函数调用的时候,需要使用栈,于是进程地址空间中存在一个专门的虚拟内存区域维护用户栈。 
(4)进程在进行动态内存分配的时候,需要使用堆,于是进程地址空间中存在一个专门的虚拟内存区域维护堆。 
(5)进程中未初始化的数据在 .bss 段 
(6)进程中初始化的数据在 .data 段 
(7)进程代码在 .text 段 
(8)进程执行的时候可能会调用共享库,在进程地址空间中有一个共享库的存储器映射区域,这个是进程独立的,因为每个进程可能调用不同的共享库。 
    linux系统中进程的地址空间分布如下图所示,其中在32位系统中0-3GB为用户空间,3-4GB为内核空间:

ELF文件中的内容布局

ELF(Executable Linkable Format)文件是linux下的可执行文件。ELF文件的结构如下所示:

其中ELF头中存放各个段的起始地址和长度以及其他的信息,各个段中存放不同属性内容。其中c程序中的变量在ELF文件中的存储区域如下图所示:

.bss段的大小可以通过ELF头中的信息得到,但是这只是一个“大小”数据,告诉程序中的非初始化的全局和静态变量会共占用多少内存,在文件中并不会有它的空间,只有当可执行文件装载运行时,才会被分配内存(并且位于data段内存块之后),并且初始化为0.

当可执行文件被装载入内存之后,各个段装载进的虚拟存储区域情况如下图所示:

内存分页

操作系统在管理内存时使用分页机制,分页机制相比于不分段不分页避免了频繁的内存调入调出,同时相对于只分段更加充分的利用了内存资源。一般内存页面大小为4KB,32位系统中进程通过虚拟内存地址访问数据或执行代码时候,32位虚拟地址中的0-11比特位为页面内的偏移量;31-12比特用于标志虚拟页面。 
    如果31-12比特共可以表示2^20个页面,因此如果使用一个页表,则会有2^20个页表项,每个页表项4B,共有4MB,因此内核空间中对每个进程都需要有4MB的空间存储页目录。而如果采用多级页表机制,31-22比特位为页目录表索引,页目录表中存放的是页表的地址,共需要2^10x4B = 4KB,每个页目录表项指向一个页表,每个页表的大小为 2^10x4 = 4KB,这样只需要在内存中存放 8KB的页目录表+一个页表就可以将虚拟地址转换到物理地址( 
进程实际执行的时候,局部性原理使得大部分内存访问可能都在一个页表所能索引到的的4KBx4K = 4MB的内存区域中) 
    在实际进行虚拟地址到物理地址的转换时,需要使用MMU,MMU是一个硬件电路,包含两个部件,一个是分段部件,将一个逻辑地址转换为一个线性地址,一个是分页部件,将线性地址转换为物理地址。

程序启动

有了以上的进程地址空间分布和可执行文件的分布信息,就可以描述进程启动过程了。程序启动时,操作系统会新建一个进程来执行该程序,主要分为三个步骤: 
(1)操作系统分配一个独立的进程地址空间,主要是在内存的内核区域中新建一个描述进程的结构体(linux中为task_struct),结构体中包含了进程的相关信息,比如进程运行状态,进程的寄存器,进程打开的资源,以及进程的内存管理结构(在linux中为mm_struct,进程的内存管理结构就描述了进程的虚拟地址空间的布局). 同时,为该进程创建一个页目录表

(2)读取可执行文件头,建立可执行文件中各个段和进程虚拟地址空间中各个段之间的映射关系。当程序运行时需要将可执行文件中的内容载入内存来执行,比如在进程访问某全局变量时,该全局变量还没有被载入内存,此时需要知道该全局变量对应在可执行文件的什么位置。于是我们就需要知道进程中虚拟地址到可执行文件中位置的对应

(3)将PC指针指向进程的代码入口处,开始执行 
    执行的时候会不断的发生缺页中断,发生缺页中断时会将实际的可执行文件中的内容载入到物理内存中,然后建立虚拟内存页和物理内存页的映射关系。

系统对进程的管理

操作系统内核区域中存储了各个进程的结构体信息,linux中为task_struct,task_struct中包含了进程的相关信息,比如进程状态,寄存器,内核栈,状态字,内存分配mm_struct。针对单独一个进程,它在运行的时候使用并更新task_struct中的信息,比如使用mm_struct用于访问内存... 
    在进程切换的时候,系统将原进程的相关信息保存到它对应的task_struct中;然后选择另一个进程,将task_struct中的信息装载到机器的寄存器中去,然后新的进程就按照它的task_struct来指导运行....

fork进程

在linux中,fork是一个系统调用,用于复制当前进程得到当前进程的子进程。fork时,系统在内核中新建一个进程结构体task_struct,由于开始时父子进程的大部分信息都相同,所以该结构体大部分信息都拷贝自原父进程的task_struct。 
    此时,子进程的虚拟内存页和父进程的虚拟内存页使用相同的物理页。当发生子进程或者父进程要对内存进行写操作时,系统再为子进程的对应的虚拟页分配物理页,即copy-on-write机制。

时间: 2024-12-19 08:53:16

进程地址空间分布和可执行文件分布的相关文章

浅析Linux计算机进程地址空间与内核装载ELF

简介: 本文基于Linux?系统对进程创建与加载进行分析,文中实现了Linux库函数fork.exec,剖析内核态执行过程,并进一步展示进程创建过程中进程控制块字段变化信息及ELF文件加载过程. 一.初识Linux进程                                                                                                                                            

浅谈进程地址空间与虚拟存储空间

早期的内存分配机制 在早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址.当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小. 那当程序同时运行多个程序时,操作系统是如何为这些程序分配内存 的呢?下面通过实例来说明当时的内存分配方法: 某台计算机总的内存大小是 128M ,现在同时运行两个程序 A 和 B , A 需占用内存 10M , B 需占用内存 110 .计算机在给

把握linux内核设计(十三):内存管理之进程地址空间

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 进程地址空间由进程可寻址的虚拟内存组成,Linux 的虚拟地址空间为0~4G字节(注:本节讲述均以32为为例).Linux内核将这 4G 字节的空间分为两部分.将最高的 1G 字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为"内核空间".而将较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF),供各个进程使用

linux进程地址空间--vma的基本操作【转】

转自:http://blog.csdn.net/vanbreaker/article/details/7855007 版权声明:本文为博主原创文章,未经博主允许不得转载. 在32位的系统上,线性地址空间可达到4GB,这4GB一般按照3:1的比例进行分配,也就是说用户进程享有前3GB线性地址空间,而内核独享最后1GB线性地址空间.由于虚拟内存的引入,每个进程都可拥有3GB的虚拟内存,并且用户进程之间的地址空间是互不可见.互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题.在前面介

把握linux内核设计思想(十三):内存管理之进程地址空间

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 进程地址空间由进程可寻址的虚拟内存组成,Linux 的虚拟地址空间为0~4G字节(注:本节讲述均以32为为例).Linux内核将这 4G 字节的空间分为两部分.将最高的 1G 字节(从虚拟地址0xC0000000到0xFFFFFFFF).供内核使用,称为"内核空间". 而将较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF),供各个进程使

Linux进程地址空间与虚拟内存

http://blog.csdn.net/xu3737284/article/details/12710217 32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G-4G是内核空间.进程的地址空间存在于虚拟内存中.虚拟内存不能被禁用. 进程地址空间 进程地址空间分为内核空间和用户空间 因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享.于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间. A.正文段.这是由cpu执行的

Windows提供了两种将DLL映像到进程地址空间的方法(隐式和显式)

调用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同.Windows提供了两种将DLL映像到进程地址空间的方法: 1. 隐式的加载时链接 这种方法需要DLL工程经编译产生的LIB文件,此文件中包含了DLL允许应用程序调用的所有函数的列表,当链接器发现应用程序调用了LIB文件列出的某个函数,就会在应用程序的可执行文件的文件映像中加入一些信息,这些信息指出了包含这个函数的DLL文件的名字.当这个应用程序运行时,也就是它的可执行文件

linux进程地址空间详解(转载)

linux进程地址空间详解(转载) 在前面的<对一个程序在内存中的分析 >中很好的描述了程序在内存中的布局,这里对这个结果做些总结和实验验证.下面以Linux为例(实验结果显示windows上的结果也一样). 我们还是利用前面看到过的这个图,如下图:32位X86机器的内存布局图,内存主要分为栈.堆.BSS段.数据段.代码段5个段.   代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分区域的大小在程序运行前就已经确定,并且内存

《Linux内核设计与实现》读书笔记之进程地址空间

1.进程的内核区域包含各种内存对象 进程的内核区域包含各种内存对象,比如: 1.可执行文件代码可以包含各种内存映射,称为代码段(text section). 2.可执行文件的已初始化全局变量的内存映射,称为数据段(data section). 3.包含未初始化全局变量,也就是bss段的零页(页面中的信息全部为0值,所以可以用于映射bss段等目的)的内存映射. 4.用于进程用户空间栈的内存映射. 5.每一个诸如c库或动态链接程序等共享库的代码段.数据段和bss段也会被载入进程的地址空间. 6.任何