一、进程地址空间
1.想当然的内存
我们在之前的学习中了解过内存的概念,所以变量都要存在内存中我们的程序才能跑起来,那么我们肯定也见过如下的示意图。
这个图是我们在之前c语言学习的时候用来了解变量存储时观看的示意图。其中,整个地址空间的方向是从下到上地址逐渐增大的,栈区在堆区之上,两个区域对向占用空间。
然后我们来实践一个奇怪的现象。
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 int val=100;
6 printf("this is oid val add: %p\n",&val);
7 printf("this is oid val : %d\n",val);
8 int d=fork();
9 if(d<0)
10 {
11 printf("开辟失败\n");
12 return 0;
13 }
14 else if(d==0)
15 {
16 val=200;
17 printf("this is new son :%d\n",val);
18 printf("this is new son add:%p\n",&val);
19 }
20 else
21 {
22 printf("this is new dad :%d\n",val);
23 printf("this is new dad add:%p\n",&val);
24 }
25 return 0;
26 }
~
我们在xshell中运行这一段代码,我们来观察观察父子进程的公共变量的地址。
我们在运行之后,输出了这一段内容,让我们大为震惊。其中可以确定的是父子进程公共变量的地址从两个进程中访问到的变量地址都是相同的,但是在变量内容发生改变的时候,从进程中查询到的地址数值竟然没有发生变化,并且在同一块地址竟然存储了两个数值,在我们的认知中,同一块物理内存是不可能存储两个数值的,所以我们断定,这个查询显示的并不是真实的物理内存空间!
2.实际的内存
实际上我们查询和分配的都不是真正的物理内存,而是一个虚拟进程地址空间。接下来我们就来详细了解了解地址空间。
1.什么是地址空间
每个进程都有自己的地址空间,也就是我们平常学习语言中所用到的所谓的地址,其本质就是一个数据结构,我们通过示意图来看。
其中地址空间就是我们用整形数字进行分界划分,每个区域都有自己相应存储的数据类型。数据表面是存储在地址空间中,实际上是存放在内存中。
其中每个进程的地址空间也都需要进行管理,于是我们在pcb中会存有地址进程空间指针,并且每个进程地址空间之间也会以链表的形式链接。
2.地址空间和内存
地址空间和内存中间通过一个映射表进行联系,这个映射表就是页表,通过页表我们的地址空间可以转换为真正的物理空间地址,存在一一对应的关系。每个进程的页表也是通过链表的形式进行连接,方便进行统一管理,同时为了保护物理内存我们的页表也是存放在CPU的寄存器中。
此时上面这段代码执行出现的情况我们就可以进行解释了:
我们在进行子进程的创建之后,我们的进程地址空间和页表都是进行浅拷贝的。也就是说在子进程刚创建好的时候,父子进程所有物理内存的地址都是共享的。可代码中,我们明明实现了变量的修改,应该会发生写时拷贝啊,为什么最后查看两个值的地址仍然是相同的呢?
这是因为进程的工作都是独立的,在创建完进程之后,我们修改变量确实是会发生写时拷贝,但是并不会改变子进程页表中的左值,只会改变右值,也就是说,实际上真正的物理内存地址已经发生改变了,只不过虚拟地址空间没有改变而已!
3.为什么要区分两种内存
让用户操作虚拟内存有下面几个好处:
- 将物理内存的管理从无序变为有序,如果是直接从物理内存上申请空间,申请的地址顺序大概率是无序的,但是进程地址空间的内存都是划分好范围,并且在申请时有严格的顺序的,比如栈区从上往下,堆区从下往上。
- 将内存管理和进程管理进行解耦,进程管理就是进程中的数据的修改,都是在页表左侧,通过虚拟地址进行,不用考虑物理内存中的顺序等等问题。内存管理就是进程执行前,提前将磁盘中的代码和数据等等复制到内存中,当需要进行开辟空间时,开辟空间也不影响左侧的进程管理。
- 保护物理内存,防止非法操作,比如野指针等等操作。
- 提高效率,一些没有必要的内存管理操作我们可以延后执行,节省时间,比如malloc和new操作,系统并不会立即去物理内存中去给你分配,而是通过先在虚拟地址分配,当你即将写入或者其他使用用途时,再真正去物理内存中分配的方法提高效率。
说到这里,我们的进程内容又得到了充实,目前看来一个进程包括他的内核数据结构和代码数据部分,其中内核数据结构包括pcb,进程地址空间和页表三个部分,对于子进程来说,内核数据结构除了pcb中特殊的一些属性之外,其余都是复制父进程。代码是共享部分和自己用if-else划分出来的一部分,数据只有写时拷贝部分。
创作不易,感谢阅读!