[Linux 进程(五)] 程序地址空间深度剖析

这篇具有很好参考价值的文章主要介绍了[Linux 进程(五)] 程序地址空间深度剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器

1、前言

Linux学习路线比较线性,也比较长,因此一个完整的知识点学习就会分布在两篇文章中,没有连贯起来,订阅的朋友谅解一下,再次感谢订阅!
上一篇文章最后讲到了程序地址空间分布,大家可以先复习一下上一篇文章:程序地址空间的初认识
本片我们深度学习一下程序地址空间,虚拟地址与物理地址的关系,页表与物理地址的映射,写时拷贝的过程,我们带着这些问题开始我们今天新的学习!

2、什么是进程地址空间?

在学习地址空间前,我们要明确:C/C++看到的地址其实并不是真正的地址,它其实是 虚拟地址,真正的地址是物理地址。
我们通过一个故事来带大家更好的理解一下什么是地址空间:
有一个大富豪,他有10亿的资产,同时拥有四个私生子,这四个私生子互相不知道其他人的存在。
私生子1是学生,私生子2是一个社会青年,私生子3是律师,私生子4是企业家。一天富豪分别对四个私生子说:
对1说:你是咱家的大学生,好好学习,以后这10个亿都是你的。
对2说:……,以后这10个亿都是你的。
对3说:……,以后这10个亿都是你的。
对4说:……,以后这10个亿都是你的。
他们都各自以为自己有10个亿等着继承呢,于是他们断断续续的对父亲说:
1:……,有用,需要xxx钱(不过10亿的1/10)。父亲给了。
2:……,有用,需要xxx钱(不过10亿的1/10)。父亲给了。
3:……,有用,需要xxx钱(不过10亿的1/10)。父亲给了。
4:……,有用,需要xxx钱(不过10亿的1/10)。父亲给了。
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器

操作系统对应富豪;
内存对应那真实的10个亿;
进程对应为私生子;
虚拟地址空间对应富豪对他们每个人画的10亿的饼。
总结:
地址空间其实并不是真实的,是操作系统给进程画的饼。

3、进程地址空间的划分

我们看一下地址空间的分布:
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
地址空间其实就是上面各个区域结合起来,这些区域的划分很简单,用begin与end两个变量一个指头,一个指尾,来限制范围即可。
根据以上的学习,我们不难得知:虽然地址空间是操作系统给进程画的的饼,但是进程多了这些地址空间我们也需要管理起来,要不然进程和对应的地址空间对应不上。说到管理就不得不提“先描述,再组织”。Linux中,这个进程/虚拟地址空间这个东西,叫做:

struct mm_struct
{
	long code_start; // 代码区开始
    long code_end; // 代码区结束
    long data_start; // 数据区开始
    long data_end; // 数据区结束
    long heap_start; // 堆区开始
    long heap_end; // 堆区结束
	long stack_start; // 栈区开始
    long stack_end; // 栈区结束
    ... // 其他属性
}

进程地址空间mm_struct里面其实就是维护了一张链表,每个节点是一个结构体vm_area_struct,里面存的就是一个区域的开始和结束地址。

4、虚拟地址与物理地址的关系

我们来写一段代码探究一下地址:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int g_val = 100;                                                                                                                
int main()                            
{                                     
    pid_t id = fork();                
    if(id == 0)                       
    {                                 
        // child                      
        int cnt = 5;                  
        while(1)                                                                                             
        {                                                                                                    
            printf("child, Pid: %d, Ppid: %d, g_val: %d, &g_val = %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);                 
            if(cnt == 0)              
            {                                            
                g_val = 200;                             
                printf("child change g_val: 100->200\n");
            }                         
            cnt--;                    
        }                             
    }                                 
    else                              
    {
        // father
        while(1)
        {
            printf("father, Pid: %d, Ppid: %d, g_val = %d, &g_val = %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
        }
    }

    return 0;
}

[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
我们知道,在没有发生对数据的改写时,父子进程共用一份代码和数据,这里子进程对数据进行了修改,父子进程读到的值不同了,父子进程的全局变量地址怎么还是相同的???
问题:
父子进程对同一个地址进行读取,竟然读出了不同的值?
我们这就能得出之前的结论:我们C/C++看到的地址是虚拟地址。
有了上面的理解,我们再来谈谈虚拟地址和物理地址的关系:
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
经过虚拟地址的学习以及这张图,我们现在就可以解释,同一个地址的值不同。
当我们fork创建了子进程的时候,子进程与父进程共享一份代码和数据,此时子进程与父进程进程地址空间与页表是一份。
当子进程对值进行修改的时,操作系统将父进程的进程地址空间与页表拷贝一份给子进程。当写入时,产生写时拷贝,重新开一块物理地址存内容,并将子进程页表中物理地址修改即可,子进程的虚拟地址没有变。因此父子进程虚拟地址相同是表象,本质物理地址已经修改了。
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器

5、页表的作用

页表的结构中还有一栏,访问权限,我们下面看一下:
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
根据此结构,我们知道了页表的第一个作用:页表的存在可以有效的进行进程访问内存的安全检查!
我们配合一段代码,大家更好理解:

char* str = "hello Linux";
*str = 'H';

我们都知道hello Linux是在字符常量区的,是不可被修改的,它不可被修改的本质就是:在页表中,访问权限被设置为r(只读权限)。并不是因为是在代码区不可被修改,内存是可以随意读写的,因为有页表权限的限制。
页表的第二个作用:将无序变有序
物理内存是不分代码区,栈区,堆区等的,数据在物理内存中的存储是无序的。
页表的存在让进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表可以将乱序的内存数据,变为有序,分门别类的规划好!
CPU中有一个寄存器CR3,它会保存当前进程的页表地址,此页表地址直接就是物理地址操作系统给用户一个映射的页表,是为了上面的两个作用,它自己是不需要的。

扩展

总结:

  • 每一个进程都是有页表的;
  • 进程切换,不仅仅是PCB与代码、数据的切换,切换时还要带走自己的进程地址空间与页表。

根据以上的学习,我们想一下,我们的内存只有16/32/64,但是运行一个几百G的大型游戏,是怎么运行的呢?
其实并不是要把这几百G的数据全部加载上来的,而是将数据边加载边执行,对挂起的一个模块数据与代码先换出,然后加载需要加载的,这样进程在内存中就动态的运行起来了。
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
这就是地址空间的分配。当我们其中一块代码被执行完,我们可以覆盖式的像物理内存中重新加载内容,或者重新开辟新的物理内存,重新建立新的映射关系,就可以让程序边加载边执行。
页表中,还有可能存在虚拟地址存在,其他都不存在的情况:
当访问到页表的虚拟地址存在,其他内容不存在时,此时访问暂停,操作系统先申请内存,再根据虚拟地址找到那个可执行程序,重新加载到内存里,并把对应的物理内存放入页表,这时再开始访问,就开始运行新加载的代码了。这个过程叫做缺页中断
[Linux 进程(五)] 程序地址空间深度剖析,Linux,linux,运维,服务器
我们其实也不难发现,这背后的一切进程是完全不知道的,是os给做的,所以页表的第三个作用:
页表 左边是进程管理,右边是内存管理。降低进程管理和内存管理的耦合度

6、为什么要有地址空间?

通过上面的学习,我们来总结一下这个问题的答案:文章来源地址https://www.toymoban.com/news/detail-804452.html

  • 1、有效的保护了物理地址的安全。通过进程地址空间+页表的映射,对不合法的操作可以有效的拦截。
  • 2、将物理地址的无序变为有序。物理内存是不分代码区,栈区,堆区等的,数据在物理内存中的存储是无序的。页表的存在让进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表可以将乱序的内存数据,变为有序,分门别类的规划好!
  • 3、降低进程管理和物理内存管理之间的耦合度。因为进程地址空间和页表的存在,物理内存可以不关心数据类型,直接对数据进行加载,这样进程管理与物理内存管理就做到了解耦。
  • 4、保证进程的独立性。因为地址空间的存在,各个进程都认为所有的内存空间都属于自己。每个进程都有自己的地址空间+页表,这两者配合实现了进程的独立性,每个不知道也不需要知道其他进程的存在!

到了这里,关于[Linux 进程(五)] 程序地址空间深度剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux之进程(四)(进程地址空间)

    目录 一、程序地址空间 二、进程地址空间 1、概念 2、写时拷贝 3、为什么要有进程地址空间 四、总结 我们先来看看下面这张图。这张图是我们在学习语言时就见到过的内存区域划分图。  下面我们在Linux下看一看内存区域是不是也是这么划分的。 可见在Linux下也是符合上面

    2024年02月04日
    浏览(41)
  • 【Linux】进程>环境变量&&地址空间&&进程调度

    主页: 醋溜马桶圈-CSDN博客 专栏: Linux_醋溜马桶圈的博客-CSDN博客 gitee :mnxcc (mnxcc) - Gitee.com 目录 1.环境变量 1.1 基本概念 1.2 常见环境变量  1.3 查看环境变量方法  1.4 和环境变量相关的命令 1.5 环境变量的组织方式 1.6 通过代码如何获取环境变量 1.6.1 命令行第三个参数 1

    2024年04月15日
    浏览(53)
  • 【Linux】进程周边006之进程地址空间

      👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.程序地址空间 1.1验证地址空间的排布  1.2利用fork函数观察当子进程修改某个共

    2024年02月04日
    浏览(40)
  • 【Linux】理解进程地址空间

    🍎 作者: 阿润菜菜 📖 专栏: Linux系统编程 ​我们在学习C语言的时候,都学过内存区域的划分如栈、堆、代码区、数据区这些。但我们其实并不真正理解内存 — 我们之前一直说的内存是物理上的内存吗? 我们先看一段测试代码: 运行结果: 我们可以注意到子进程的变量

    2024年02月02日
    浏览(53)
  • Linux--进程地址空间

    1.线程地址空间 所谓进程地址空间(process address space),就是从进程的视角看到的地址空间,是进程运行时所用到的虚拟地址的集合。 简单地说,进程就是内核数据结构和代码和本身的代码和数据,进程本身不能访问物理地址,之时候就需要有一个中间媒介,就是地址空间,

    2024年02月11日
    浏览(43)
  • 浅析Linux进程地址空间

    现代处理器基本都支持虚拟内存管理,在开启虚存管理时,程序只能访问到虚拟地址,处理器的内存管理单元(MMU)会自动完成虚拟地址到物理地址的转换。基于虚拟内存机制,操作系统可以为每个运行中的进程创建独享的虚拟地址空间,在这个空间中执行的程序,无法感知

    2024年01月20日
    浏览(38)
  • 『 Linux 』进程地址空间概念

    在c/C++中存在一种 内存 的概念; 一般来说一个内存的空间分布包括 栈区 , 堆区 , 代码段 等等; 且内存是 自底向上 (由 0x00000000 至 0xFFFFFFFF ); 以该图为例: 该图即为常见的内存分布图; 正文代码段 正文代码段所存放的数据 一般为函数体的二进制代码 ; 已初始化数据区 已初始化

    2024年02月03日
    浏览(45)
  • 【Linux】—— 进程地址空间

    序言: 在上篇中,我们讲解了关于进程优先级的概念。本期,我将给大家介绍的是关于进程地址空间的话题。 目录 (一)程序地址空间回顾 (二)代码演示 (三)进程地址空间的引入 总结 我们在学习C/C++语言的时候,大家可能都见过这样的空间布局图: 一个程序有哪些

    2024年02月15日
    浏览(41)
  • Linux:进程地址空间

    目录 1.程序地址空间  2.进程地址空间 我们在讲C/C++语言的时候,32位平台下,我们见过这样的空间布局图 我们来验证一下这张图的正确性: 运行结果: 通过观察静态变量的位置,可以认为静态变量就是全局变量,只是静态变量只初始化一次,有作用域的限制。 这里栈区还

    2024年02月04日
    浏览(40)
  • 【Linux】深挖进程地址空间

    作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等 座右铭:松树千年终是朽,槿花一日自为荣。 目标:熟悉【Linux】进程地址空间 毒鸡汤:也许有一天,你发觉日子特别的艰难,那可能是这次的收获特别的巨大。 望小伙伴们点赞👍收藏✨加关注哟💕💕      

    2024年02月03日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包