【操作系统】进程的内存映像

进程的内存映像,也就是说一个进程在运行的时候它在内存里边是什么样子,为了方便大家理解我们结合这个C语言程序来解释,                                                                                                           

假设这个计算机系统是三十二位的,也就意味着每个进程拥有4gb 的虚拟地址空间,那地址的范围就应该是从0000 0000一直到ffff ffff .那可以看到在这个示意图当中最上面高地址的这1gb 会用于存储操作系统的一些内核数据比如说进程的 PCB,再比如说进程的页表,另外操作系统的代码也是存储在高地址的这1 gb 当中,比如进程调度就属于内核代码,这些代码就是存储在这片区域。

这是高地址的1 gb,那我们作为普通程序员我们写的这个程序它只能访问用户区的这片数据而不能直接访问操作系统内核区的这些数据。

接下来我们来研究一下用户区里边存储了哪些数据,首先在低地址的 这个部分会有一片我们不会使用的区域,地址范围就是从0000 0000一直到0804 8000这个地址,然后从这个地址到 c000 0000这一整片的区域就会被我们的用户进程所使用,我们从低地址往高地址来看。地址最低的这片区域会用于存储只读代码以及只读数据,我们知道我们写的这些代码在正式运行之前会经过编译链接等等一系列的处理把这些代码翻译为与之等价的机器指令, 那这些机器指令在程序运行过程当中是不会改变的,所以它属于只读代码。在程序运行过程当中只能读这些 机器指令而不能写这些机器指令。因此我们在 c 语言里面写的这些程序代码最终会被翻译为机器指令然后在程序运行的时候是被存储在紫色的这片区域当中,那除了这些指令代码之外还有一些只读的数据也是存储在这片区域,在 c 语言当中有这样的一个关键字叫做 const, 用const 关键字去修饰一个变量那么这个变量就是常变量,这种变量的值在程序运行过程当中是不可以改变的。也就说对于这类的变量只能读它的数据而不能写它的数据,所以但凡 const 关键字修饰的这些变量都会被存储在紫色的这一片区域当中。

在这片区域之上会有一片区域用于存储可以读也可以写的那些数据,结合 c 语言程序来说就是我们熟悉的全局变量和静态变量。那在这个程序当中,a 这个变量是被定义在 main 函数之外它属于全局变量所以 a 存储在绿色这片区域,另外在main 函数里边这儿有个变量 c 它用 static 的关键字修饰属于静态变量,所以 c 这个变量它是被存储在绿色这片区域的,那这片区域的数据就是既可以读也可以写,

刚才提到的这两片数据区域在程序运行的时候或者说进程启动的时候就可以确定这两片区域的大小是多少,并且在整个进程运行的过程当中,它们的大小是固定不变的。这也很好理解,因为一个程序它的代码一旦写完之后这个程序对应的指令它的大小是多少就确定了。另外整个程序当中它到底定义了多少个常变量,在程序启动的时候 也可以确定。 另一方面我们写的代码当中总共定义了多少个全局变量总共定义了多少个静态变量,这些在进程启动的时候就可以确定的,并且在整个程序运行的过程当中这些数据的大小是不会改变的。                                                                               

继续往上看在读写数据这片区域上面就会有一片区域称为堆,英文是 heap ,                                   

那结合 c 语言来看,在 c 语言当中但凡用 malloc 和 free 这两个函数去处理的那些数据就是存储在堆这片区域当中,显然堆这一片数据区域它的大小肯定是可以动态的增加或者减少的。

对于三十二位的系统来说,堆区最多只能到4000 0000这个地址,也就是说堆加上刚刚提到的这三片区域总共只有1 gb 的大小,六十四位的系统它 可以 malloc 的空间还会更大,这就不展开细聊了。

回到上面这个例子,整个地址空间总共有4 gb 操作系统用掉了1 gb 然后低地址的这些部分用掉了1gb,现在中间还剩下2 gb 的部分。那这2gb 的低地址部分是共享库的存储映射区,结合我们熟悉的 c 语言来看,我们在 c 语言当中经常会调用一些系统提供的库函数,

那这些库函数的指令代码就会存储在黄色的这片区域当中,比如说这个地方使用到的print 函数,它的指定代码就是存储在共享库的存储映射区,显然不同的程序它有可能使用到的库函数数量是不一样的。你使用的库函数越多,那么这片区域肯定也会越大越往上增长。

那这是黄色的这片区域,接下来再往上看从中间的这两 gb 高地址的部分这个部分会有一片区域用来存储用户栈

我们知道程序运行的时候就是从 main 函数开始 ,main函数可能会调用其他的函数。那每调用一层函数,就需要保存上一层函数的信息同时也需要开辟一 片新的空间去存储被调用函数的相关信息,那所有的和函数调用相关的数据就存储在用户栈这片区域当中,显然这片区域也是会动态的增加或者减少的,你调用的函数层数越多,那么这个用户栈肯定也会越往下面这个方向去增长,那我们在每一个函数里边定义的局部变量以及函数调用的一些调用参数等等这些东西就是存储在用户栈里面,这就是进程的内存映像。

最后我们还需要解释一个东西,我们在 c 语言当中经常会用这种宏定义的方式定义一些常量,很多同学可能会误以为既然它是常量所以它应该被存储在紫色的这片区域当中。比如说 X 的值等于 1024,那么有没有可能在紫色的这片区域当中分配出四个字节用于存储 x 这个常量呢。

事实上这种理解是不对的,我们宏定义的常量不会在内存当中专门的分配存储的空间,那在预编译的阶段你的编译器会把这个X 把它自动的替换为1024,替换之后这个值会被隐含到程序指令当中,这个学过计算机组成原理的同学应该能够理解。在计组当中有一种寻址方式叫做立即数,也就是说程序指令当中就直接包含了1024,而如果没学过计组的同学可以不用深究。总之这儿想强调的就是宏定义的常量它并不会存储到紫色这片区域当中,用 const 关键字定义的这种常变量它会被存储到紫色这片区域当中。大家只需要记住这个结论就可以。

这就是进程的内存映像。

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值