网站建设贰金手指下拉,网站开发所使用的浏览器,业务员自己掏钱做网站可以吗,外贸soho建网站目录 1.程序地址空间
1.1.程序地址空间的介绍
1.2.程序地址空间的本质
2.进程地址空间 3.Linux下的地址空间 1.程序地址空间
1.1.程序地址空间的介绍
我们在学习C/C时#xff0c;对于各组分的地址分配在程序地址空间的不同模块 如图我们能够验证各组分的对应的地址排布位…目录 1.程序地址空间
1.1.程序地址空间的介绍
1.2.程序地址空间的本质
2.进程地址空间 3.Linux下的地址空间 1.程序地址空间
1.1.程序地址空间的介绍
我们在学习C/C时对于各组分的地址分配在程序地址空间的不同模块 如图我们能够验证各组分的对应的地址排布位置但是我们可以看到堆是向上排布栈是向下排布这个怎么验证呢 所以我们验证了堆的开辟是有下到上栈的开辟是由上到下堆栈相对而生并且我们可以发现堆栈之间有一块大的空白这也是栈溢出发生的原因 另外在栈区中我们知道地址是由高到低排布的然而一个整型变量 int a; 由4个字节构成我们知道一个地址对应着1个字节那么可以推出int类型实际上由4块连续的地址来实现的那么int类型在栈上是如何排布的呢存在两种情况a是低地址 或者 高地址 接下来我们看看数组的地址分布 我们看到arr[1]在arr[0]上边也就是只有地址向上排布时才能够符合。 那么就有“栈空间的开辟是自上向下而空间内变量的使用是自下向上增长” 回到对int a的分析 那么以最低地址为起始地址也就是a的本质就是这4个字节的最低的那个地址对于double类型就是8个字节的最低地址也就是通过 变量名 变量类型 来确定对应变量的地址空间。 换一句话来说地址空间通过“起始地址偏移量”来访问不同的变量类型 讲了这么多大家也只是知道了程序地址空间用来存储变量地址的空间
1.2.程序地址空间的本质 #include stdio.h
#include stdlib.h
int global_val 200;
int main()
{pid_t id fork();if(id0){printf(fork error\n);return 0;}else if(id 0){printf(it is child process, pid is: %d , g_val is: %d , g_val_address is: %p \n, getpid(), global_val, global_val);}else{printf(it is father process, pid is: %d , g_val is: %d , g_val_address is: %p \n, getpid(), global_val, global_val);}return 0;
}这里符合我们之前学习的父子进程在没有对数据写入时共用一份代码和数据那么同一个变量也是在同一块地址。 在上述代码的基础上我们在子进程模块将g_val值修改再来看看 #include stdio.h
#include stdlib.h
int global_val 200;
int main()
{pid_t id fork();if(id0){printf(fork error\n);return 0;}else if(id 0){global_val 100;printf(it is child process, pid is: %d , g_val is: %d , g_val_address is: %p \n, getpid(), global_val, global_val);}else{printf(it is father process, pid is: %d , g_val is: %d , g_val_address is: %p \n, getpid(), global_val, global_val);}return 0;
}与上部分代码相对比我们发现一个地址两个变量 我们在学习地址时每一个物理地址只能对应一个变量而这里的“0x60104c”却对应着两个不同的g_val的值所以这个地址一定不是物理地址而是虚拟地址。 也就是程序地址空间实际上是抽象出来的虚拟地址。 我们在之前fork()函数初识时知道fork()成功调用时的返回值可以为id0id0两个值本质上就是子进程可以继承父进程的虚拟地址也就是实际上id就可以看做上面的全局变量global_val共享一块虚拟地址那么他们的物理地址呢这里我们先不多讲剩下的留在下面讲解。
2.进程地址空间 每一个进程运行之后都会有一个进程地址空间的存在 我们在上面知道程序地址空间实际上就是抽象出来的虚拟地址实际上进程地址空间也是抽象出来的虚拟地址。 因为实际上的数据是存储在物理内存中的而地址空间是抽象出来的模块并没有存储数据的能力也就是地址空间最终需要通过映射关系到物理内存每一个进程都有一个映射表。 实际上父进程在创建子进程后会和子进程共用同一个物理内存当父子进程数据发生改变时物理内存中会进行数据的“写时拷贝”进而产生一块新的地址并通过页表再映射回去同一个虚拟地址。这就是父子进程的地址空间会出现一个地址对应两个变量的原因实际上还是两块物理地址。 3.Linux下的地址空间
地址空间实际上就是一个抽象的模块连接着进程和物理内存并且是由操作系统所管理的那么不就是一个“先描述再组织”结构体吗 我们发现通过抽象出进程的地址空间这个模块对于所有的进程均是同一在这个数据块上实现的并且按照代码中的每一个模块把每一部分都有序地放置在地址空间内在通过页表映射在物理内存中。那么我们在磁盘中加载程序进入物理内存中时就可以随意加载在物理内存的任意位置因为我们只需要通过页表就能进入一个有序的进程地址空间也可以在维护好的进程地址空间的映射找到对应的物理内存。 这里我们可以看出“地址空间有助于我们将无序的地址变为有序的进程的地址空间的控制” 另外页表除了存放着虚拟内存和物理内存两个字段外还存放这一个“物理内存访问权限”字段 除了有访问权限字段页表中有一个字段用于判断物理内存是否分配当我们在运行程序时进程可能处于挂起状态也就是代码和数据从物理内存放回磁盘中这时候我们把这个字段的进行修改表示这个进程不在物理内存中。那么进程在查询页表时发现这个字段为挂起就能够在进程管理模块中判断该进程为“挂起状态”进程的状态是进程管理而挂起实际上是内存层面上的又因为我们把地址空间和物理内存独立成块了。
所以地址空间的引入实现了“进程管理和内存管理的解耦”