0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux程序地址空间详解

dyquk4xk2p3d 来源:入门小站 2023-03-26 10:39 次阅读

1 代码感受

在正式讲程序[地址空间]前我们先来看一段简单的代码来分析分析:

1#include
2#include
3usingnamespacestd;
4
5intg_val=100;
6
7intmain()
8{
9pid_tid=fork();
10if(id==0)
11{
12//child
13while(true)
14{
15cout<<"我是一个子进程,我的pid是:"<

大家可以自己先分析一下结果。

我们来运行一下结果:

01d0501a-ca69-11ed-bfe3-dac502259ad0.png

大家看前面几行可能就会立马发现问题:我们定义的 g_val 是全局变量,当子进程修改 g_val 的值时我们发现父进程的 g_val 是不受影响的,那么说明父子进程所用的 g_val 并不是同一个变量(这个很好理解,之前的我们说过父子进程是相互独立的,互相不受干扰的),但是问题出现在最后一列,我们惊奇的发现居然父子进程的 g_val 变量的地址居然是相同的,前面不是说父子进程的 g_val 不是同一个变量吗?这里为啥打印出来的地址会是相同的呢?

这里就说明我们打印出来的地址并不是真正的[物理地址],我们语言层面打印出的地址叫做虚拟地址或者线性地址。我们在用C/C++语言所看到的地址,全部都是虚拟地址!而物理地址,用户一概看不到,由OS统一管理 。OS必须负责将虚拟地址转化成物理地址。

2 进程地址空间

首先我们来讲一个故事:从前有一个企业家很有钱,他的家产大概有一亿美金左右的样子。他有 4 个私生子,并且这四个私生子互相并不知道对方的存在。第一个私生子是个学霸,在国内顶尖学校上学,这个富豪便对他说,你要好好读书,将来我这一亿美金全部都是你的;第二个私生子是一个三线演员,富豪便对他说,我帮你打开你红的渠道,你不要辜负了我对你的期望,好好努力,将来我这一亿美金都是你的;第 3 个私生子是个女儿,当的是小学老师,富豪便对他说,你也不用太过努力工作,我就你这一个女儿,等我老了这一亿美金就是你的了;第四个私生子是一个初中的小混混,富豪对他说,你只要好好听我的话,这一亿美金就是你的了。

富豪给每个私生子都做出了承诺要将一亿美金给他们,但是实际富豪并没有那么多的钱给每个私生子一亿美金,而这一亿美金就是富豪给私生子们画的一张大饼,但是它的私生子们却信以为真。

那这个故事与我们讲的知识有什么关系呢?其实操作系统就是那个富豪,私生子们就是一个一个的进程,而那一亿美金就是进程地址空间。

PS: 我们在生活中要尽量少画饼。

操作系统给进程画了一张大饼,操作系统的资源是有限的,所以他就得要好好的把这张饼给管理起来,不让这些进程乱来,而如何管理呢?

那就要先描述,再组织,Linux 中用的是一种叫做 mm_struct 的内核数据结构来管理的。

我们来用一张图带大家来看看程序地址空间:

02f7097a-ca69-11ed-bfe3-dac502259ad0.png

这张图相信大家多多少少也不会陌生,在 C 语言的学习中我们也见到了很多次。

那么程序地址空间如何编码的呢?(32 位的平台下[虚拟地址]空间大概是 4GB)

ps: 下面图每个小空格代表着一个字节。

03145214-ca69-11ed-bfe3-dac502259ad0.png

所以从这里我们也不难看出为啥虚拟地址也叫做线性地址。那么我们究竟是如何管理虚拟地址空间的每个区的呢?

我们可以用下面这种方式来描述管理:

structmm_struct
{
longcode_start;
longcode_end;
longinit_start;
longinit_end;
…………
longbrk_start;
longbrk_end;
longstack_start;
longstack_end;
}

而_start 和_end 限定的区域就是叫做虚拟地址(线性地址)

那么问题来了,既然上面我们讲了那么多虚拟地址,真正的物理地址又在哪里呢?

我们画一个图方便大家理解:

035326ba-ca69-11ed-bfe3-dac502259ad0.png

通过这张图大家并不难发现,我们在语言层面上的地址是地址空间的虚拟地址,而虚拟地址要与物理地址建立映射,就需要一张页表(页表的工作原理我们将放到后面来讲)。

我们在学习 C 语言时大家在书上看到这样的一句代码:const char* str="hello world";

这时书上会告诉大家这句 str 指向的内容是只读的,不可修改的,但是这时为什么呢?这时我们就可以自己来分析分析:str 指向的内容是在常量字符区,当常量字符区通过页表与物理地址建立映射时在页表中就将该数据设置为只读,当我们后续有修改操作时就会直接报错。

有了上面的基础我们就可以来解释解释为啥开头我们的 g_val 是同一个地址,但是指向的内容却不相同的问题了:

037def76-ca69-11ed-bfe3-dac502259ad0.png

当不修改数据时就不会发生写时拷贝,父子进程指向的是同一块物理空间 (为了节约资源);当要修改数据时就会发生写时拷贝,父子进程指向的是不同的物理空间,但是虚拟地址空间是相等的。

我们再来回答为啥 fork 会有两个返回值的问题就很容易了,就是因为父子进程的返回值是不同的,所以肯定会发生写时拷贝将不同的返回值用相同的虚拟地址来进行返回,虽然虚拟地址是相同的,但是他们通过页表建立映射的关系却是不一样的。

到目前为止,程序地址空间的基本内容已经 ok, 接下来给出一些扩展。

3 扩展

首先引出一个问题:假如没有程序地址空间,OS 是如何工作的?

我们知道如果没有了地址空间,那么 cpu 将直接跟物理地址打交道,这样做的后果是什么?

我们不难知道假如 cpu 直接跟物理地址打交道的话那么当我们从 cpu 中读到非法地址时那就坏了,通过非法地址将我们程序中其他变量的值给修改了那不就扯淡了吗。所以我们要通过一层屏障来保护数据,而这一层保护就是通过程序地址空间来进行的,当我们访问的数据非法时通过页表的映射就会拒绝你的非法操作。

所以我们得出了程序地址空间的第一个好处:防止地址随意访问,保护物理内存和其他进程。

在向大家提出一个小问题:当我们在堆上 new 空间时 OS 是立马就把空间给你,还是等你需要的时候再给你?

这个问题大家应该都能够答对,与我们想得一样,OS 会在我们需要该空间的时候再去在堆上申请。

03db0d0a-ca69-11ed-bfe3-dac502259ad0.png

而页表暂时没有与物理内存建立映射关系称作页表中断,当我们需要空间的时候再与 · 物理内存建立映射。大家从这张图看出来没有,当我们通过页表建立映射时将进程管理与内存管理给解耦合了。我进程管理不需要关心你是怎样在内存上申请空间的,内存管理也不需要关心进程是如何管理起来的,这样下来维护成本就会变得更低,维护效率会更加高效一些。

所以我们得出了程序地址空间的第二个好处:将进程管理与内存管理进行解耦合。

再提出一个问题:程序在被编译的时候没有被加载到内存,那么程序内有没有地址呢?

答案是有的。源代码再被编译的时候就是按照虚拟地址空间的方式将对应的代码和数据进行编制,编译器也会遵守虚拟地址的规则。

当我们把程序加载到内存,程序里保存的地址(虚拟地址,并不是程序本身在内存中的物理地址) 就会被 cpu 读取, cpu 通过虚拟地址找到对应的虚拟地址空间,然后虚拟地址空间又通过页表映射到物理内存中,这样就将程序的整个运转给联系起来了。

所以我们得出了程序地址空间的第三个好处:可以让进程以统一的视角看待自己的代码和数据。

最近很多小伙伴找我要一些程序员必备资料,于是我翻出了压箱底的宝藏,免费分享给大家!

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1372

    浏览量

    40282
  • Linux
    +关注

    关注

    87

    文章

    11296

    浏览量

    209360
  • 操作系统
    +关注

    关注

    37

    文章

    6808

    浏览量

    123292
  • 程序
    +关注

    关注

    117

    文章

    3785

    浏览量

    81009
  • 代码
    +关注

    关注

    30

    文章

    4780

    浏览量

    68539

原文标题:Linux:程序地址空间--原来操作系统也喜欢画大饼

文章出处:【微信号:良许Linux,微信公众号:良许Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内核地址映射模型与Linux内核高端内存详解

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为
    发表于 05-08 10:33 3453次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>地址</b>映射模型与<b class='flag-5'>Linux</b>内核高端内存<b class='flag-5'>详解</b>

    Linux设备驱动开发详解

    #《Linux设备驱动开发详解》电子书连载#第8章 Linux设备驱动中的阻塞与非阻塞IO,阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活地支持用户
    发表于 06-25 15:14

    基于嵌入式Linux应用程序开发详解

    基于嵌入式Linux应用程序开发详解
    发表于 10-25 14:17 12次下载
    基于嵌入式<b class='flag-5'>Linux</b>应用<b class='flag-5'>程序</b>开发<b class='flag-5'>详解</b>

    基于嵌入式Linux的LCD驱动程序设计

    Linux有内核空间和用户空间,平时工作在保护模式,每个应用程序进程都有自己的虚拟地址空间,应用
    发表于 07-27 07:31 1612次阅读
    基于嵌入式<b class='flag-5'>Linux</b>的LCD驱动<b class='flag-5'>程序</b>设计

    如何为Linux编写用户空间设备驱动程序

    了解如何为Linux编写用户空间设备驱动程序。 用户空间驱动程序为某些设备提供内核空间驱动
    的头像 发表于 11-22 07:04 3754次阅读

    高端内存的详解linux用户空间与内核空间

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为
    发表于 04-28 17:33 984次阅读
    高端内存的<b class='flag-5'>详解</b>:<b class='flag-5'>linux</b>用户<b class='flag-5'>空间</b>与内核<b class='flag-5'>空间</b>

    Linux用户空间与内核空间

    应用程序运行在用户空间,而Linux 驱动属于内核的一部分,因此驱动运行于内核空间。当我们在用户空间想要实现对内核的操作,比如使用open
    发表于 05-20 10:58 1029次阅读
    <b class='flag-5'>Linux</b>用户<b class='flag-5'>空间</b>与内核<b class='flag-5'>空间</b>

    深入浅出Linux的进程地址空间

    我们知道,在32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G-4G是内核空间。其实,这个4G的
    的头像 发表于 06-20 09:57 1940次阅读

    Linux操作系统知识讲解:走进linux 内存地址空间

    Linux操作系统知识讲解:走进linux 内存地址空间
    的头像 发表于 08-28 10:45 5041次阅读
    <b class='flag-5'>Linux</b>操作系统知识讲解:走进<b class='flag-5'>linux</b> 内存<b class='flag-5'>地址</b><b class='flag-5'>空间</b>

    PCI总线地址空间与系统地址空间的关系

    1、PCI地址空间 PCI总线具有32位数据/地址复用总线,所以其存储地址空间为2的32次方=4GB。也就是PCI上的所有设备共同映射到这4
    的头像 发表于 01-06 08:30 2341次阅读

    为什么进程地址空间中要包括操作系统(内核)呢?

    这张图就是Linux程序运行起来后所谓的进程地址空间,这里包括我们熟悉的代码区、数据区、以及堆区和栈区。
    的头像 发表于 04-18 09:09 1084次阅读

    Linux操作系统中程序地址空间详解

    在正式讲程序[地址空间]前我们先来看一段简单的代码来分析分析。
    发表于 09-12 10:56 445次阅读
    <b class='flag-5'>Linux</b>操作系统中<b class='flag-5'>程序</b><b class='flag-5'>地址</b><b class='flag-5'>空间</b><b class='flag-5'>详解</b>

    Linux系统为什么需要引入虚拟地址

    Linux 系统中,采用了虚拟内存管理技术,事实上大多数现在操作系统都是如此!在 Linux 系统中,每一个进程都在自己独立的地址空间中运行,在32 位系统中,每个进程的逻辑
    的头像 发表于 10-07 17:28 946次阅读
    <b class='flag-5'>Linux</b>系统为什么需要引入虚拟<b class='flag-5'>地址</b>

    Linux虚拟地址空间和物理地址空间的关系

    过程,这其实也是MMU的工作原理。 我们知道,在Linux中,每个进程都有自己独立的地址空间,且互不干扰。每个进程的地址空间又分为用户
    的头像 发表于 10-08 11:40 1198次阅读
    <b class='flag-5'>Linux</b>虚拟<b class='flag-5'>地址</b><b class='flag-5'>空间</b>和物理<b class='flag-5'>地址</b><b class='flag-5'>空间</b>的关系

    linux驱动程序运行在什么空间

    Linux 驱动程序是操作系统的一部分,负责管理硬件设备与操作系统之间的交互。驱动程序运行在内核空间(Kernel Space),这是操作系统的核心部分,与用户
    的头像 发表于 08-30 14:37 392次阅读