初识--Linux的虚拟地址空间

目录

重新了解地址空间

前提提要:简要介绍一下fork函数

奇怪的事情

进程地址空间

页表&虚拟进程地址空间

而操作系统为什么要这么做呢


重新了解地址空间

在学习c/c++语言的时候,大家一定见过以下这张图

说的是程序会加载在如图的结构上,实际上,我们真的对他很了解吗,而在Linux进程控制这,就会有一个奇怪的现象

前提提要:简要介绍一下fork函数

进程=内核数据结构(PCB)+自己的代码以及数据

在Linux中,fork可以从当前进程创建一个新进程,创建的新进程为子进程,而当前当前进程就是子进程的父进程,而子进程会以父进程为模板,创建子进程

头文件在#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核做:
1.分配新的内存块和内核数据结构给子进程
2.将父进程部分数据结构内容拷贝至子进程
3.添加子进程到系统进程列表当中

4.fork返回,开始调度器调度

这里先不深入讲解fork函数,主要是为接下来的行为做铺垫

请看以下代码

                                                                                                                              #include <stdio.h>                                                                                                                                                 
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;//全局变量

int main()
{
  pid_t id = fork();//创建进程
    
  if(id < 0){//创建进程错误
  perror("fork");
  return 0;
 }
  else if(id == 0){ //子进程返回码为0 这里就是子进程
  printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);//getpid返回当前进程的pid
 }else{ //父进程
    printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}

运行结果也如我们所料

parent[19376]: 0 : 0x601050
child[19377]: 0 : 0x601050

 子进程和父进程的g_val的地址都是相同的,很正常.

奇怪的事情

如果我们对以上代码的两个进程的其中一个进行修改,会发生什么呢,大家不妨大胆猜测一下

                                                                                                                              #include <stdio.h>                                                                                                                                                 
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;//全局变量

int main()
{
  pid_t id = fork();//创建进程
    
  if(id < 0){//创建进程错误
  perror("fork");
  return 0;
 }
  else if(id == 0){ //子进程返回码为0 这里就是子进程

   g_val+=10;//我们在子进程这修改g_val的值

  printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);//getpid返回当前进程的pid
 }else{ //父进程
    printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}

在这里 我们修改了全局变量g_val的值,嗯,因为是全局的,g_val地址对应的值只有一个,所以父子进程都应该打印g_val=10 对吧? 我们来看看实际的结果

parent[19907]: 0 : 0x601050
child[19908]: 10 : 0x601050

欸 不是哥们,啊? 你这俩进程全局变量的对应地址不都是一样的吗,咋打印出的值还不一样啊,难道技术创新了,一个地址可以对应两个值? 当然不是的,接下来,我们就来讲讲为什么会出现这种奇怪的现象 

物理地址所对应的值有且只能有一个,所以我们可以退出以下结论

1.变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

2.但地址值是一样的,说明,该地址绝对不是物理地址
3.在Linux地址下,这种地址叫做 虚拟地址
4.我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由操作系统OS统一管理

那么------------> 也就是说 操作系统必须负责将 虚拟地址 转化成 物理地址,


进程地址空间

所以之前说‘程序的地址空间’是不准确的,准确的应该说成进程地址空间,那该如何理解呢?

那么操作系统是如何将进程虚拟地址转换成物理地址的呢?

页表&虚拟进程地址空间

操作系统其实是利用一种页表的操作将进程虚拟地址空间映射到物理地址空间,而这个页表去映射的过程,其实就有点类似于哈希的映射的关系

上面的图就足矣说明问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被页表映射到了不同物理地址!!!

操作系统为什么要这么做?

这样做有两个好处

1.将物理内存从无序转换成有序,让进程以统一的视角,看待内存

2.将进程管理内存管理进行耦合

地址空间+页表也就是保护内存安全的一个重要手段

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值