Linux进程地址空间

在前面的文章中我们在讲述fork函数的时候,遇到了这样的问题,先有一个变量,然后使用fork函数创建子进程,在子进程中对这个变量进行修改会发现虽然子进程与父进程打印出来的值是不一样的但是这两者的地址居然是相同的。

 Linux进程地址空间_第1张图片

我们知道进程具有独立性而进程 = 内核数据结构 + 代码和数据,为了保证进程的独立性,继续保证内核数据结构、代码和数据都要有独立性。假设这个地址为物理地址,但是在一个地址中存储两个数据是不可能的,这说明这个地址并不是真是存储数据的地址。这里使用的地址被称为虚拟地址或线性地址。

进程地址空间

进程地址空间本质上就是一个内核数据结构 struct mm_struct{}。Linux进程地址空间_第2张图片

每一个进程有task_struct和地址空间mm_struct。在地址空间中表征的就是进程看待内存的方式,但是最终的数据还是要落实到真正的物理内存中。未来在访问需要的内容的时候,要现在虚拟空间中寻找对应区域。

在这个地址空间的数据结构中,其中的代码区、数据区、堆区这些区域该怎样的理解?

struct area
{
    int start;
    int end;
}

地址空间本质上就是一个线性区域,这个区域的宽度代表一个字节,其中划分各个区就是区域划分。假设这里的计算机为32位,计算机中各种设备都需要通过总线连接起来,CPU和内存连接的线被称为系统总线,这里假设有32根总线,每一个线只能有两种状态,那么32根线一共可以表示2^32中排列组合,就可以出现2^32个地址再乘上对应的宽度1字节,就可以得到内存为4GB。编址的时候是从0x0000...0到0xFFFF...F这个地址是连续的,因此地址空间也应该是连续的。每个数字表示一个地址,每个地址就是一个字节,开辟整数时就是要是用四个地址,但我们在使用的时候会取的是最小的地址,类型的存在就可以表明要取几个字节,相当于偏移量。在操作系统进行编写的时候,就有针对32位域64位的源代码,编译时会自动识别CPU是多少位的。Linux进程地址空间_第3张图片

 限定了区域之后,区域之间的数据就叫做虚拟地址/线性地址。

前文中讲述了虚拟地址,但是数据和代码只能存在内存中,因此在虚拟地址和物理地址之中还需要有一种叫做页表的技术。Linux进程地址空间_第4张图片

对于之前的问题,就可以有了一个简要的解答: Linux进程地址空间_第5张图片

子进程的虚拟地址是继承于父进程的,而两者被映射到了不同的物理地址,因此会打印出不同的数据,也可以说明进程具有独立性。这同样可以说明,明明只定义了一个id,但是ifelse的结果都可以运行。当fork在返回的时候父子都有了,会return两次。id是pid_t类型的变量,而返回的本质就是写入,谁先返回,谁就让OS发生写时拷贝。

为什么有地址空间

1、防止地址随意访问,保护物理内存与其他进程(页表中还有与权限控制相关的内容)。

2、将进程管理与内存管理进行解耦合。

malloc的本质:向OS申请内存的时候,操作系统不会马上给你,因为操作系统一般不允许任何的浪费或不高效;在申请成功之后到使用之前,空间没有被你使用,但是别人也是用不了,就会处于闲置状态。对于这种机制,成为缺页中断。这样可以使进程管理与内存管理实现解耦合。

3、可以让进程以统一的视角,看待自己的代码与数据。

程序在编译的时候没有被加载到内存,程序的内部也是有地址的,源代码在编译的时候,就是按照虚拟地址空间的方式进行对代码和数据早就已经编好了对应的编址。(ELF格式)Linux进程地址空间_第6张图片

 CPU读到的数据中涵盖地址,这个地址是虚拟的。

你可能感兴趣的:(Linux,linux)