【Linux】程序地址空间?进程地址空间

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

程序地址空间回顾

了解进程的运行:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main()
  5 {
  6     pid_t id = fork();
  7     if(id<0)
  8     {
  9         printf("fork error\n");
 10         return 1;
 11     }
 12     else if(id==0)
 13     {
 14         printf("子进程\n");
 15     }
 16     else
 17     {
 18         printf("父进程\n");
 19     }                                                                                                                                                                                                         
 20     return 0;
 21 }

 运行结果:我们会发现这打印的结果乱七八糟,因为它也不知道什么时候该干什么
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
 我们让代码睡眠1秒:打印的结果就正常了
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
 以前我们学习的内存管理(程序地址空间):
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间

 为了验证上面虚拟地址,我们运行下面代码:

int global_value = 100;

int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        printf("fork error\n");
        return 1;
    }
    else if(id == 0)
    {
        int cnt = 0;
        while(1)
        {
            printf("我是子进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(1);
            cnt++;
            if(cnt == 10)
            {
                global_value = 300;
                printf("子进程已经更改了全局的变量啦..........\n");
            }
        }
    }
    else
    {
        while(1)
        {
            printf("我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(2);
        }
    }
    sleep(1);
}

【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
(这种问题出现的原因在下面的为什么要存在虚拟地址空间有讲述)

多进程在读取同一地址的时候,怎么可能出现不同的结果?

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 这里地址没变化,说明这里的地址一定不是物理地址,我们以前学习的语言的基本地址(指针)不是对应的物理地址,而是虚拟地址(线性地址)(逻辑地址),这三种说法在Linux之中是相等的,但是在别的地方它们是不同的概念。
  • 在Linux地址下,这种地址叫做 虚拟地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理

 打印出来的地址空间排布,全部都是虚拟地址

感性理解虚拟地址空间:
  我们将进程地址空间类比 “大饼” ,而这个“大饼”就是操作系统给进程画的进程它认为自己是独占系统资源(事实上并不是)。假如我是老板(操作系统),我要给我的员工(进程)画饼,员工(进程)要被管理,每个员工的大饼不尽相同,那我们给员工画的大饼(地址空间)也要被管理,进程要被管理-先描述再组织。所以说,地址空间的本质:是内核的一种数据结构(mm_struct)(PCB也是内核的一种数据结构)


进程地址空间

所以之前说‘程序的地址空间’是不准确的,准确的应该说成 进程地址空间

什么是进程地址空间?

在操作系统中,进程地址空间是进程可以访问的虚拟地址范围。它是操作系统分配给进程的连续虚拟地址块。进程地址空间用于存储进程的代码、数据和堆栈。

每个进程都有自己的专用地址空间,这意味着没有两个进程可以访问同一个虚拟地址。这样做是为了确保进程不会相互干扰。操作系统使用称为内存保护的机制来强制实施此分离。

 进程地址空间分为多个区域,每个区域都有不同的用途。例如,代码区域包含进程的可执行代码,数据区域包含进程的数据,堆栈区域包含进程的调用堆栈。

操作系统通过将虚拟地址映射到物理地址来管理进程地址空间。这是使用称为页表的数据结构完成的。页表是将每个虚拟地址映射到物理地址的表。当进程尝试访问虚拟地址时,操作系统会在页表中查找该地址以查找相应的物理地址。

 进程地址空间是操作系统中的一个关键概念。它允许操作系统将进程彼此隔离并管理系统的内存资源

有关进程地址空间的一些其他注意事项:

  • 进程地址空间的大小通常受系统上可用的物理内存量的限制。
  • 操作系统可以使用各种技术来管理进程地址空间,例如分页和交换。
  • 进程地址空间可以使用共享内存和进程间通信 (IPC) 等机制在进程之间共享。

如何使用进程地址空间的一些示例:

  • 创建进程时,操作系统会为其分配新的进程地址空间。
  • 从文件加载进程时,操作系统会将文件的内容映射到进程的地址空间。
  • 当进程进行系统调用时,操作系统会将进程的虚拟地址转换为物理地址,然后执行系统调用。
  • 当进程终止时,操作系统将释放进程的地址空间。

如何理解区域划分:

【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间

  • 地址空间描述的基本空间大小是字节
  • 32位下,2^32次方个地址 、2^32 * 1字节 = 4GB空间“范围”
  • 每一个字节都要有唯一的地址
  • 2^32个地址是虚拟地址,只要保证唯一性即可

 实例出mm_struct的对象:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
 定义局部变量,malloc new堆空间,是扩大栈区或堆区
 函数调用完毕,free,是缩小栈区或堆区

 这个mm_struct就是操作系统给进程画的“大饼”,告诉进程这4G空间全是给你的。

进程地址空间与PCB、物理内存、页表和磁盘之间的关系

 进程地址空间是进程内存的逻辑视图。它是操作系统分配给进程的连续虚拟地址范围。进程地址空间用于存储进程的代码、数据和堆栈。

 PCB(过程控制块)是一种数据结构,其中包含有关进程的信息,例如其进程ID,父进程ID,状态,优先级和地址空间。(Linux操作系统下的PCB是: task_struct)

 物理内存是计算机的实际内存。它用于存储当前正在运行的所有进程的代码,数据和堆栈。

 页表是将虚拟地址映射到物理地址的数据结构。操作系统使用它来将进程使用的虚拟地址转换为硬件可以访问的物理地址。

 磁盘是辅助存储设备。它用于存储当前未运行的代码,数据和进程堆栈。

进程地址空间与PCB、物理内存、页表、磁盘的关系如下:

  • PCB包含一个指向进程地址空间的指针。
  • 进程地址空间由操作系统使用页表映射到物理内存。
  • 如果没有足够的物理内存可用,操作系统可以将页从进程地址空间交换到磁盘。
  1. 创建进程时,操作系统会为其分配新的进程地址空间。进程地址空间最初为空。然后,操作系统将进程的代码和数据从磁盘加载到进程地址空间中。

  2. 当进程运行时,操作系统使用页表将进程地址空间映射到物理内存。这允许进程访问其代码、数据和堆栈。

  3. 当进程未运行时,操作系统可能会将页从其进程地址空间交换到磁盘。这将为其他进程释放物理内存。

  4. 恢复进程时,操作系统使用页表将页从其进程地址空间加载回物理内存。这允许进程从中断的位置继续运行。

 进程地址空间、PCB、物理内存、页表和磁盘都是现代操作系统的重要组成部分。它们协同工作以允许多个进程在一台计算机上同时运行。

Linux内核代码:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
 这个就是存放起始值:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
磁盘与内存的IO,进程虚拟地址的工作原理:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
操作系统给每个进程画的“大饼”:就是每个地址空间都是2^32。实际上OS每次只会给你申请那么多,它也知道你其实也差不多就用那么多,你要是申请多了,就不让你申请成功,而不是真的给每个进程都4GB空间。
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间

为什么要存在虚拟地址空间?

  1. 如果让进程直接访问物理内存,万一进程非法越界操作呢?
    【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
  2. 地址空间的存在,可以更方便的进行进程和进程的数据代码的解耦,保证了进程独立性这样的特征

  我们分析一下上面出现的这种问题:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
 子进程是由父进程为模板创建的,把父进程的PCB拷贝给子进程,把父进程的地址空间也拷贝给子进程,因为是拷贝过来的,所以在同样的地址有这个global_value。

 因为进程具有独立性,一个进程对被共享的数据做修改的时候,如果影响了其他的进程,那么就不能称为独立了。所以,当子进程要修改global_value的值的时候,在物理内存中会首先将它的值拷贝一份在另外的空间,然后将子进程的页表的映射更改为这个拷贝后的空间的地址,然后再更改它的值,整个过程不会影响到父进程的值,同时也没有影响虚拟地址空间的地址,所以我们父子进程打印出来的&global_value的地址没有变换。

 上面的这种方式叫做写时拷贝,他是操作系统自动做的,对不同进程的数据进行分离。操作系统为了保证进程的独立性,做了很多工作,通过地址空间,通过页表,让不同的进程,映射到不同的物理内存处。

进程 = 内核数据结构 + 进程对应的代码和数据 (两个都是独立的,所以说进程具有独立性)

  1. 让进程以统一的视角,来看待对应的代码和数据等各个区域,方便使用编译器也已统一的视角来进程编译代码(规则是一样的,编完即可直接使用)

重新理解地址空间

 我们写一个调用函数:

int a = 10;
fun()
{
	use a;(仅代表使用a)
}
main()
{
	fun();
}

且看下面调用逻辑:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间
总结:

  1. 我们的可执行程序在没有加载到内存的时候在内部已经有地址了,是在代码的编译期间按照2^32(32位机器)空间进行编址,属于是逻辑地址。所以我们平时说的在32位机器下编译,在64位下编译,差别就是编址空间不同。
  2. 虚拟地址空间,不要只认为操作系统会遵循对应的规则,编译器也要遵守,编译器在编译代码的时候,就是按照虚拟地址空间的方式对我们的代码和数据进行编址的。
  3. 我们将磁盘的代码加载到物理内存,就天然具备了一个物理地址。此刻我们有两套地址,一个是标识物理存在中的代码和数据的地址,一个是程序内部互相跳转的时候的地址-虚拟地址
  4. debug运行起来CPU内部使用的都是虚拟地址

程序运转:

  1. 我们首次运行不是将main函数的物理地址加载进入CPU,而是靠mm_struct里面已经对应好的start将其虚拟地址加载入CPU
  2. main函数的虚拟地址加入后我们通过地址空间,然后页表映射到物理内存上,我们找到main函数后向下执行到调用fun(),然后是将fun();这个指令读取到CPU内部,而不是直接加载物理地址,这个指令内部就有地址(虚拟地址),然后重复上述运行。
  3. 整个过程中CPU都没有见到物理内存

感性的理解上述虚拟地址、页表、物理地址关系:
  我们将虚拟地址认为是你的学号,页表为你的班长,你的宿舍号就是物理地址。你住进学校宿舍就天然就有了一个宿舍号(物理地址),你的辅导员(CPU)使用学号(虚拟地址)需要找到你,就通过你的班长(页表),来找到你的宿舍号最后找到你,你的辅导员(CPU),不关心你的宿舍号(物理地址)是什么,只关心你的本人学号(虚拟地址)。

进程地址空间:
【Linux】程序地址空间?进程地址空间,Linux基础,linux,面试,进程地址空间


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀文章来源地址https://www.toymoban.com/news/detail-540846.html

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

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

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

相关文章

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

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

    2024年02月04日
    浏览(28)
  • 【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日
    浏览(41)
  • 【Linux】进程周边006之进程地址空间

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

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

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

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

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

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

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

    2024年02月03日
    浏览(26)
  • 『 Linux 』进程地址空间概念

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

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

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

    2024年01月20日
    浏览(29)
  • 【Linux】理解进程地址空间

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

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

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

    2024年02月04日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包