【Linux】进程地址空间

这篇博客要谈的是进程地址空间,什么意思呢?我们之前在学习C语言的时候可能见过这张图

这张图中显示的不是物理内存而是虚拟内存,就是我们说的进程地址空间。在这张图中我们可以看到,堆是从低地址到高地址的,栈是从高地址到低地址的,并且它们是相对而生的,我们也可以通过代码来验证一下

通过这批代码和结果我们可以和清楚的看到它们之间的关系,下面我们再来看一下环境变量和命令行参数的位置,它们是在栈上面的,我们写一个这样的代码

我要解释一下&argv[i]和argv[i]的区别

argv[i]中存的就是这个字符串的首地址,而&argv[i]中存的是存放这个首地址的地址,也就是说,无论是命令行参数还是环境变量,它们都是靠一张表存的,这张表中存的是每个字符串的地址

我们可以看到从上到下,地址是依次增大的,也就是说,无论是上面说的表,还是表中地址指向的内容都是在栈上部的

我们之前说过static修饰的变量,说它只初始化一次,后面再次遇到就不初始化了,我们可以看看它存放在哪个位置

我们可以看到,它之所以跟我们的普通变量不一样,就是因为它是存在全局数据区的,它其实就已经是全局变量了

有了上面的基础,下面我们看一个奇怪的现象,就是这样的一个代码

子进程改变g_val之前还是正常的,改变之后居然g_val的值不同,但是地址竟然是相同的,这就说明这里的地址绝对不是物理地址,因为如果是物理地址那么是多少就是多少,绝对不会出现两个不同的值,所以这里的地址就是虚拟地址,也叫线性地址,相同的虚拟地址会经过各自的页表映射到不同的物理地址。其实凡是我们用到的地址都不是物理地址,包括我们之前说的同一个id,值不同使父子进程分流,这自然也是映射到不同的物理地址。

其实每个进程都有一个进程地址空间,这其实是操作系统给进程画的一张“大饼”,就像上面的图一样,说是有4G,每个进程也认为它们有4G空间,但实际上怎么可能每个进程都有4G呢,进程实际上是用不了这么多内存的,所以这就是假的,这是操作系统想让进程看到的,等进程真的需要空间时,进程看来就是去虚拟内存中申请,操作系统看来就是从物理内存中申请,然后通过页表将虚拟和物理内存映射起来。

那么这个进程地址空间也要管理,比如分区域,操作系统是如何管理的呢?先描述再组织,所以其实进程地址空间就是特定的数据结构具体到内存中,就是特定数据结构的对象,那么它是如何管理的呢,其实就是通过每个区域的start和end来进行管理的,在源代码中我们也可以看到确实是这样的,并且这里的start和end只代表一个数字,就通过这个数字来表示虚拟内存,

我们所说的这个虚拟内存其实就是单纯的数字范围,它是不具备对于数据和代码的保存能力的,实际的物理内存才有这个功能,所以要找到一种物理内存和虚拟内存的映射关系,这个映射关系就存放在页表

到这里,我们知道了有虚拟内存和页表的存在,那么它们存在的意义是什么呢?

1.让物理内存从无序变为虚拟内存的有序,让进程以统一的视角看待内存。也就是说,我从物理内存中申请的空间可能不是连续的,但是映射到虚拟内存就是连续且有规律的了,此时每个进程看待内存就都是是各自的栈堆等这些相同的空间了

2.将进程管理和内存管理进行解耦合,就是它们之间的关系并没有那么强了,进程看来就是去申请空间,操作系统看来就是去开辟一块空间,它根本不需要考虑进程的问题,比如进程要求空间连续,操作系统也可以申请不连续的空间,只需要映射到连续的空间即可,它们之间的工作就相对独立了

3.这是保护内存安全的重要手段,比如我们之前遇到的野指针问题,就是不是你的空间是不让你访问的,这就是在页表这一层来拦截住的(页表中不只有映射关系,其实还存有权限)

页表肯定也是有地址的,它就存放在CR3这个CPU中的寄存器中,我们之前的图片中也有这个寄存器,

下面说一些new/malloc这些动态开辟内存的一些问题

我们动态开辟完内存后可能不会立即使用,所以其实操作系统为了效率和资源的利用率,可能在使用new后不会立马去物理内存中开辟内存,而是在进程的虚拟地址空间中去开辟,等到真正要去写入数据时,才会去物理内存中开辟空间。这样好处是提高物理内存的利用率并且提高new和malloc这个函数的速度,因为单纯用这个函数是不用去物理内存中开辟空间的。

我们上面说页表不止存映射关系,还有比如这块空间的权限,比如有只读,只写或读写,这个权限其实在写时拷贝时就用到了,什么是写时拷贝,就是当父进程创建子进程后,如果子进程不写入数据,那么它就是用父进程的数据,但是一旦要写入数据,就要拷贝并写入数据。为什么要先拷贝呢?因为我改的可能只是原数据的一部分,所以一开始肯定要和父进程数据相同

就是一开始这块空间是只读的,然后子进程要写入数据了,权限不足,这是我们就叫缺页中断,然后让操作系统来进行处理,操作系统一看这是正常的操作,于是就开辟空间并且把权限改成读和写,这时父子进程就可以进行这个数据的各自写入了。

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值