• 【lesson12】理解进程地址空间


    什么是进程地址空间?

    故事:
    背景有一个大富豪,家里的存款有10亿美元,他有三个私生子三个人之间彼此互不相识,只有富豪知道他们的存在。
    人物介绍
    在这里插入图片描述
    有一天富豪分别找他们谈话:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    从谈话中我们可以看出,富豪分别对他三个儿子画了个大饼。而三个儿子之间也不知道彼此的存在,所以他们就相信了。
    我们类比到今天到学习的内容:
    在这里插入图片描述
    在这里插入图片描述
    那么饼要不要管理呢?
    要!
    例子公司的老板给所有人都画过大饼,这天老板给张三说表现不错下次给你升经理。第二天老板又说张三干的不错,昨天我跟你说过干的好升部长没忘记吧。这时候张三懵了,不是经理吗,怎么又变部长了。

    所以我们就会明白饼也是要管理的。
    如何管理:
    先描述,再组织。

    内核中的地址空间,本质将来也一定是一种数据结构。将来一定要和特定的进程关联起来

    进程地址空间的作用

    历史计算机是直接访问物理内存的。
    在这里插入图片描述
    物理内存本身是随时可以被读写的。
    而这就导致了一个问题:特别不安全。
    如果进程A内的代码要跳转到进程B,这如果进程A崩溃可能也会导致进程B的崩溃,这样进程间就没有独立性了。

    现代计算机提出了一下的方式
    在这里插入图片描述
    这是可能就会有人有疑问这不还是要访问物理地址码?
    万一虚拟地址是非法的呢?
    举故事说明:
    背景过年了张三收到了每个亲戚的压岁钱,加起来一共有五百块。妈妈不放心于是找张三商量
    在这里插入图片描述
    于是接下来小明有需要的时候,就找妈妈拿钱。
    在这里插入图片描述
    在这里插入图片描述
    我们可以看到妈妈是可以拒绝我们非法的请求的。
    在这里插入图片描述
    这里我们可以推广到今天讲的内容
    张三:虚拟内存
    妈妈:映射机制
    超市:物理内存

    如果地址非法,映射机制禁止映射也变向的保护了物理内存。

    虚拟地址究竟是什么?
    映射关系的维护究竟是谁做的?
    这两个问题之后再讲解。

    我们都知道进程地址空间有区域划分,区域被划分为了:代码区、堆区、栈区等等区域,那么如何理解区域划分。

    故事理解
    假设小胖和小美是同桌他们共用一张桌子。
    在这里插入图片描述
    而小胖不注意卫生每天脏兮兮的,于是小美就在桌子上画了一条线,让小胖不能越界。
    在这里插入图片描述
    假设桌子长100cm
    [0,50]属于小胖
    [51,100]属于小美

    而小美划分桌子的本质就是在做区域划分。
    那么是如何用代码实现区域划分的呢?
    在这里插入图片描述

    每个进程都有地址空间,地址空间是一种内核数据结构,它里面至少要有各个区域的划分。
    在这里插入图片描述
    所以所谓的区域划分本质就是在一个范围里定义start和end,而所谓的范围变化本质就是对end和start进行加或者减一个特定的范围值。
    Linux具体的区域划分:
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    只要做到每个进程的页表,映射的是物理内存的不同区域,就能做到进程直接不会互相干扰,保证进程独立性。

    解释初识地址空间博客中的一个现象:
    同一地址的g_val有不同的值。
    在这里插入图片描述
    我们可以看到当父进程和子进程分别进行映射的时候,他们各自的页表分别把它们映射到不同的物理地址所以一个g_val才会有两个值
    两个进程直接g_val的虚拟地址可以是一样的。

    同时也发生了写时拷贝。

    这也回答了我们之前学进程的时候fork有两个返回值的问题。
    在这里插入图片描述

    return会被执行两次,return的本质就是对id进行写入,此时就发生了写时拷贝,所以父子进程各自在物理内存中有属于自己的id变量空间。只不过在用户程用同一个变量(虚拟地址)来标识。

    扩展内容

    初步理解

    当我们程序在编译的形成可执行程序,没有被加载到内存中的时候。
    请问我们程序内部有地址吗?

    在这里插入图片描述
    我们看到我们并没有运行可执行程序,就已经有地址VMA了,而VAM就是虚拟地址空间
    所以可执行程序编译的时候内部其实已经有地址了!
    所以地址空间不仅仅是OS内部要遵守,其实编译器也要遵守!
    即编译器编译代码的时候,就已经给我们形成了各个区域:代码区,数据区等等。并且采用和Linux内核中一样的编址方式,给每个变量,每一行代码都进行了编址。故程序在编译的时候,每一个字段早已经具有了一个虚拟地址!!!
    在这里插入图片描述

    程序内部的地址依旧用的是编译器编译好的虚拟地址。
    当程序加载到内存的时候,每行代码,每个变量具有了一个物理内存地址。

    当CPU读到指令的时候指令内部有地址,这个地址就是虚拟地址。

    深入理解

    在这里插入图片描述

    这里就有问题地址空间和业表最开始的时候数据是从哪里来的?
    当数据要被加载到内存的时候,可执行程序要用编译好的虚拟地址填充进入其中。
    每一个函数和变量都有地址->编译器给的
    同样的也一定被加载带物理内存中。

  • 相关阅读:
    HTML属性,标签
    【0128】【创建postgres后端进程】Latch的实现机制与原理分析(8-2)
    SpringBoot启动流程大揭秘
    vue如何打包上传至服务器?
    接口测试 —— jmeter与数据库的操作
    NAACL2022中Prompt相关论文分类
    一个WPF开发的、界面简洁漂亮的音频播放器
    快手新财报:曙光就在冲刺路上?
    日常开发小汇总(5)数组克隆、伪数组转换为真数组、随机排序
    typescript 基础一篇掌握(一万四千字攻略总结)
  • 原文地址:https://blog.csdn.net/m0_67077469/article/details/133362890