linux之《程序地址空间》

问题导入

学过C/C++的人都知道,在程序里面一个地址空间就只能存放一个变量的值,这是正确的。那么我们来看看下面这段代码:

#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int g_value = 0;
int main()
{
    pid_t id = fork();
    assert(id >= 0);
    if(id == 0)
    {
        // 子进程
        while(1)
        {
            printf("子进程,pid=%d,ppid=%d,g_value=%d,地址为:%p\n",getpid(), getppid(), g_value, &g_value);
            ++g_value;
            sleep(1);
        }
    }
    else
    {   
        // 父进程
        while(1)
        {
            printf("父进程,pid=%d,ppid=%d,g_value=%d,地址为:%p\n",getpid(), getppid(), g_value, &g_value);
            sleep(1);
        }
    }
    return 0;
}

在这里插入图片描述
由程序得知,g_value是全局变量,地址都相同,但是为什么他们的值却不一样呢?

带着这个问题,我们来学习一下程序地址空间。

一,虚拟地址

  1. 我们在各种语言里面使用的指针和取得的地址,其实都是虚拟地址,而真正的地址是物理地址,是通过虚拟地址根据某种关系来找到的,是操作系统来完成的事情,我们用户是不能直接获取的

  2. 虚拟地址是连续的

我们通过几张图片来理解:
在32位系统中,地址的大小是0~ 232-1字节;64位则是0~264-1字节。32位系统与64位系统在这里举例是没有区别的,为了方便画图,我们以32位系统为例。
此图为简略内存地址图,不是准确的

图上就是地址空间的大致分部(详细可以见此),值得注意的是,这个是虚拟地址,地址是连续的,因此操作系统会定义一个数据结构(如下)来管理这些虚拟地址的分部。

struct mm_struct
{
    //代码段
    long code_start;
    long code_end;
    
    //……
    
    // 堆区
    long brk_start;
    long brk_end;
    // 栈区
    long stack_start;
    long stack_end;
}

二,页表

前面说了,用户是无法直接接触到物理地址的,平时在程序里取到的都是虚拟地址,数据都是存在于物理地址的,那么虚拟地址与物理地址又有什么关系呢?
通过下图,我们来理解:
在这里插入图片描述

虚拟地址通过页表和MMU转换成物理地址

这里的转换过程就不展开来讲了,详细过程可以自行百度。

三,解决问题

通过前面引入了这么多概念的我们现在可以对前面的问题进行解答了。

1. 父子进程的关系

fork()函数调用的时候,会生成子进程,每个进程都会有自己的页表,而子进程的绝大部分数据都是复制父进程的,所以会出现它的全局变量g_value的地址与父进程的一样,变量g_value的虚拟地址会在页表中转换成相应的映射关系。

2. 写时拷贝(写时复制)

全局变量g_value在被子进程修改时,会在物理内存申请一块地址来复制全局变量的值,这就是写时拷贝。

在这里插入图片描述
这里我们来整理一下,子进程在修改全局变量的值时,系统会在内部再开辟一个物理内存来存储修改后的值,修改完后,子进程页表的映射关系发生改变,但是虚拟地址仍然不变。这样就发生了一个地址存储不同变量的现象,事实上这里的地址不是物理地址,而是虚拟地址。

四,虚拟地址的重要性

  1. 防止地址随意访问,保护物理内存和其他进程

  2. 将进程管理和内存管理进行解耦合在这里插入图片描述

malloc的用法原理

向系统申请一块内存,但系统不会马上给你分配内存,他是等到这块内存真正被用到时,才会分配好地址。具体体现在页表的变化上面,malloc申请内存时,页表的一边会给你分配好虚拟地址,另一边先不做变化,等到真正使用申请到的内存时,系统才会完善好页表。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值