【Linux操作系统】进程地址空间(虚拟内存、物理内存)

在这里插入图片描述
之前我们说这个图是程序地址空间,那它是内存吗?
答:根本不是的
它准确来说叫进程虚拟地址空间!

为了方便理解我们用一段代码来看一下

#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){ //child
        printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    else{ //parent
        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
}

结果:

parent[1234]: 0 : 0x1234567
child[1235]: 0 : 0x1234567

当把代码修改以下:

#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){ //child
        g_val=100;
        printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    else{ //parent
        sleep(3);
        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
}

结果:

parent[2345]: 0 : 0x1234567
child[2356]: 100 : 0x1234567

我们有个惊奇的发现!

父子g_val的不相同,但是地址竟然一样!很明显这已经是两个变量了,按道理应该是两个不同的地址,但是地址相同,说明&打印出来的地址根本不是物理地址,而是虚拟地址!
物理地址,用户一概看不到,由OS统一管理。
OS必须负责将 虚拟地址 转化成 物理地址 。

在这里插入图片描述

进程地址空间

我们在语言层面遇到的地址都是虚拟地址!

每个进程都有一个地址空间,都认为自己独占物理内存。

在这里插入图片描述
进程地址空间跟进程一样都得被数据化,也是先描述再组织,所以要描述地址空间,就要有一个结构体数据结构类型。(struct mm_struct)。

每个进程都认为地址空间划分是按照4GB空间划分的,都认为自己独有这4GB。

在这里插入图片描述

所有虚拟地址是地址空间上进行区域划分时,对应的线性位置。

虚拟地址到物理地址

在这里插入图片描述

为什么不让PCB直接去访问物理地址呢?
答:task_struct如果可以直接访问物理地址,有没有想过,物理地址有很多进程,如果你不小心寻址错误,访问到了其他进程,而这个进程是转账类似的,那是不是很危险?而如果我们有一层中间层,不允许你直接访问物理地址,而是在这之间对你的请求进行检查,如果合法给你映射过去,不合法就中止你的请求。

就像const char* s=“hello world”
你想*s=‘H’,这样是不允许的,当页表识别出你是字符常量区的,它映射时就不会给你w的权限,本质上就是OS给你的权限只有r权限。

而这就是说为什么要有地址空间?

第一个原因:
通过添加一层软件层,完成有效的对进程操作内存进行风险管理(权限管理),本质目的是为了保护物理内存以及各个进程的数据安全。

再者说:
如果我想申请1000字节空间,我们立马就可以得到吗?
不一定,可能我们只是单纯告诉OS我要申请空间,以后我要用,但是现在不一定用。那在OS角度,如果空间给你,你又不立马用,或者用不完,那岂不是这部分空间就闲置着?别人想用也用不了。所以地址空间的第二个作用就来了!
在这里插入图片描述
这叫基于缺页中断进行物理内存申请!

第二个原因:
将内存申请和内存使用的概念在时间上划分清楚,通过虚拟地址空间来屏蔽底层申请内存的过程,达到进程读写内存和OS进行内存管理操作进行软件上面的分离。

你别管我最后咋给你在物理内存上开辟,反正给你开了就行,如果当你要的时候物理内存不够了,我就进行内存管理算法给你腾出地方。

再者说最后一个原因:
cpu说:我想知道我该运行哪个进程了,这个进程的开始地址是啥。
不同进程的main()地址也不同,cpu还得自己找,所以cpu不乐意了,我就在0x00000000位置拿进程地址,我不找了,你想让我运行哪个,你给我送到这。
所以进程地址空间就一直在0x00000000处存进程地址,在页表建立映射关系。
在这里插入图片描述

第三个原因:
站在cpu和应用层角度,进程统一可以看作,它们各自独有4GB虚拟空间,而且这个空间区域相对位置是比较确定的。因为地址空间的存在,程序的代码和数据可以被加载到物理内存的任意位置,通过映射到地址空间,而地址空间的地址是连续的,大大减少了内存管理的负担。

OS最终目的就是:让每个进程都独享4G空间,这样每个进程都统一起来,方便管理。

现在我们再来看这张图:
在这里插入图片描述
父子进程本来是共享代码和数据的。子进程的创建是以父进程为模板的。
而进程也是独立的。

当有人要修改数据,那么为了保证进程的独立性,发生写时拷贝,在物理内存新开辟一片空间,其实在物理内存,子进程的g_val地址已经改变,但是映射到虚拟地址上,虚拟地址没有改变,只是页表到物理内存的映射关系变一下。
所以最开始的现象就解释通了。

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周周汪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值