文章目录
进程地址空间
1. 回顾C/C++地址空间
1.1 提出问题
C/C++学习中,对于变量分配相对地址中的格局也就是C/C++的地址空间已经有了大致的印象:
那么对于这个所谓的C/C++地址空间是什么呢?是内存吗?在我们以前学习指针的刻板印象中的确如此,但其实并非如此
1.2 观察此问题产生的现象
写如下代码, 运行观察其结果
运行结果:
我们会发现,前几行运行结果全局变量的值都是100,后因为子进程修改全局变量的值,父子进程g_value值不同,但是两者的地址却相同,同一块地址空间竟然出现了两个不同的值,与我们之前学习指针的概念大相径庭,于是我们就可以怀疑这里的地址空间绝对不是真实的物理地址空间
1.3 解释现象
由上面现象也可以推出曾经我们学习C/C++的基本的地址(指针)也一定不是对应的物理地址!因此对于这个现象得出了一个结论:我们语言层面所看到的地址都是虚拟地址(也可称为线性地址)!
因此解答我们开头的问题,上述我们所看到的地址分布也不是真正的物理内存上的空间。
2. 虚拟地址空间
2.1 感性理解虚拟地址空间
设计进程的理念——进程它会认为自己是独占系统资源的(事实上并不是)
引入一个小故事
1.背景:
在美国有一个拥有10亿美金的富豪,他有4个私生子,A(大儿子: 做生意) , B(二女儿: 卖化妆品),C(三儿子: 哈佛读书), D(小儿子: 高中辍学混社会), 他们4人彼此都不知道对方的存在, 富豪为了让孩子们各司其职, 给每个人都单独许下一个承诺: 努力工作/读书,我就只有你一个孩子, 将来去世后, 这10亿美金都由你来继承。4个孩子听到后都非常开心并对富豪说自己会努力工作/读书。
2.经过
A要去见重要客户,为了撑场面问富豪要50万美金买豪车和名表;B的化妆品需要投入一些资金也问富豪要50万美金;C上学问富豪2000生活费;对于这些小钱富豪并不在意,都给他们了。就这样A,B,C三人一点一点要钱,但是有一天D直接问富豪要5亿美金,富豪拒绝了。这时D会生气吗? 不会的,因为他知道迟早有一天这些资产都是自己的,毕竟富豪就他这么一个私生子,自己也确实太着急了,这件事就不了了之了。
那么这其中,富豪对应的就是操作系统,它所拥有的十亿美金就相当于内存,这4个私生子就分别对应4个进程而这4个孩子问富豪要钱,就相当于进程找操作系统要内存(也可称为对象空间),而对于经过的最后一个情节,就好比进程找操作系统要16G的内存空间(一般的电脑一共就16G),操作系统会直接给你16G的空间吗?那是不可能的,因为操作系统还要兼顾其他的进程,但每一个进程被操作系统拒绝后,也仍然会认为自己拥有全部的内存的使用权。因此,操作系统给进程画的大饼,就是进程(虚拟)地址空间。
即我们可以感性的认为:进程(虚拟)地址空间就是操作系统给进程画的“大饼”
2.2 如何"画饼"
就像上面故事中的富豪一样,每次孩子问自己要钱他要不要记录下谁要过钱后还剩下多少钱呢? 要的, 否则他去世时,给某个孩子多少钱就说记不住了,所以这个饼也是需要被管理的;饼的本质就是一种内核数据结构
那么如何管理呢? 先描述,再组织
地址空间的本质:是内核的一种数据结构:mm_struct,因此就可以通过管理mm_struct结构体从而管理地址空间。
2.3 地址空间的区域划分
共识:
达成共识之后,接下来就需要引入地址空间区域的划分了,为了能够更好理解,引入一个小故事
小胖和小花两人互为同桌,因小胖不讲卫生并且经常欺负小花, 于是小花忍无可忍揍了小胖一顿并在桌子的中间画了一条边界线,要求小胖不能越界否则就揍他,于是小胖只能答应了,就这样小花和小胖各自占有桌子的一半。
同桌各占一半桌子就是一种区域划分, 可以用结构体去表示
struct area { int start; int end; };
小胖和小花所占有区域用结构体变量去表示
struct area xiaohua_area = {1, 50}; struct area xiaopang_area = {50, 100};
故事继续,小胖和小花和平相处一段时间后,小胖又调皮了,开始超线并且欺负小花,小花忍无可忍又揍了小胖一顿并且重新画了一条边界线,原本是一人占据一半,桌子长100 cm, 现在小花多占30 cm, 小胖只能占20 cm, 小花的举动就是一种扩大区域的行为
修改结构体变量中的成员可以模拟扩大区域的行为
xiaohua_area.end=80; xiaopang_area.start=80;
桌子在规定的界限范围内,放置书包、课本等物品,也就对应了我们在计算机系统的虚拟内存中指定的区域进行数据的分配。而小花和小胖也就对应mm_struct
结构体。用小花小胖的区域划分理解虚拟地址空间的区域划分,实际上就是在 mm_struct
中定义变量来划分区域
小花扩大区域的行为就可以对应修改mm_struct
中成员变量的值,其实地址空间的动态分配就是通过改变边界的大小来实现的
更深刻的理解文章开头的问题,
mm_struct
中如果限定了区域, 那么区域与区域之间的数据就是虚拟地址
3. 深入理解进程地址空间
3.1 是什么
存储的数据在虚拟地址空间上通过页表将虚拟地址映射到物理内存上,就是物理地址
3.2 解决问题(怎么做)
文章开始有一个这样的问题, 同一块地址空间父子进程打印竟然出现了两个不同的值
开始没有修改g_value前, 父子进程的虚拟地址相同,g_value值相同
子进程想要修改 g_value时, 操作系统会在内存中重新申请一块空间先将100放入,并且重新构建映射关系,不让子进程再指向父进程曾经的数据了,指向新的地址,要修改时直接修改新指向地址里的数据,不会影响父进程。修改时只影响页表的K, 不影响虚拟地址,即虚拟地址不变
总结: 因为父子进程映射到不同的物理地址处,访问数据当然不同。但不影响虚拟地址,所以虚拟地址不变