【linux进程(七)】程序地址空间深度剖析

简介: 【linux进程(七)】程序地址空间深度剖析

1. 前言

由于此Linux系列文章偏向于做

顺序学习手册,所以有些内容在

一篇文章中可能不连贯,敬请谅解!

请先阅读下面的文章的最后一段
了解打印出来的地址并不是物理地址
这一事实逻辑:

程序地址空间前言

本章重点:

本篇文章着重讲解进程中的虚拟地址
和物理地址的关系,了解虚拟地址的
内核本质是什么,以及页表和物理地址
的映射逻辑和写时拷贝的具体体现,
期间我们将回答三个问题:

  • 什么是地址空间?
  • 地址空间是如何设计的?
  • 为什么要有地址空间?

2. 什么是程序地址空间?

在理解地址空间前,我们知道一个事实:

平时看见的地址并不是真实的地址

又叫虚拟地址,真实的地址空间

被称为物理地址(物理内存)

我想通过讲一个故事来帮助理解
到底什么是程序地址空间:

有一个富豪,它有10亿资产,由于
年轻时比较浪,所以他有四个私生子
这四个私生子并不知道彼此的存在,
私生子A是个医生,私生子B是个企业家
私生子C是个街头混混,私生子D是个学生
富豪分别对小A,B,C,D说:
(1),小A啊,你要是努力做个医生,以后我的
10亿美金都是你的了
(2),小B啊,要是你把你的公司运作的很好
以后我的10亿美金就是你的了
(3),小C啊…小D啊…

故事还没结束,有一天,A说:老爸
给我10万美金,我要买医疗器械,
富豪想了想觉得是正事,于是给了
小A十万美金,小C又说:老爸,给我
两千美金吧,我吃不起饭了,富豪一听
就把钱打过去了,所以我们知道,这四个
人都可以用10亿美金以内的钱,但是
永远用不到10亿美金!

现在列出人物和地址的对应关系:

  1. 富豪对应操作系统
  2. 10亿美金对应物理地址
  3. 私生子对应每一个进程
  4. 富豪画的饼对应地址空间

所以可以得出结论,地址空间可以
理解为操作系统给进程画的饼
它并不是真实的物理地址!


3. 程序地址空间是如何设计的?

既然富豪给每一个私生子都画了饼
所以对应每一个进程都又一个自己的
程序地址空间,这是第一个结论

然而,OS要管理这些空间,一定
要先组织,再管理!所以地址空间
是一个struct结构,**这是第二个结论**

再来看看程序地址空间的图:

程序地址空间无非就是各个
区域的结合,然而各个区域的划分
无非就是两个整数begin和end
一个在区域的开头,一个在区域的结尾
这是第三个结论!
如图:

根据上面的推论,我们可以得出结论:
地址空间这个北河数据结构中(struct)
至少包含了各个区域的划分,它的概念
结构如下:

struct addr_room
{
  int code_start;//代码区起始
  int code_end;//代码区结束
  int init_start;//初始化区起始
  int init_end;//初始化区结束
  int heap_start;//堆区起始
  int heap_end;//堆区结束
  ......
  其他属性
};

所谓的区域范围变化
实际上就是对start和end做加减!


4. 虚拟地址和物理地址的关联

当我们理解了什么是虚拟地址后,

现在再谈虚拟地址和物理地址的关系:

本质是一种映射关系!

现代计算机使用以下方法解决问题:
OS为每一个进程配对一个虚拟地址
空间和一张页表,要访问物理地址时,
需要先在页表进行映射,若访问的是
非法地址,则会在页表层阻止你的访问!

所以为什么一个地址会有两个值?

现在我们就能回答这个问题了:

创建子进程时,子进程的数据和
代码和父进程共享,也就是和父进程
一样,所以此时子进程的页表和程序
地址空间和父进程一样!当子进程尝试
修改变量的值时,操作系统会重新为子进程
开辟一份物理内存,并修改子进程页表的
映射关系,此时映射到物理内存的不同
区域,但是虚拟地址是一样的!

这个过程也叫写时拷贝


5. 页表的结构以及作用

修改常量字符串时编译器会报错,

是在哪个阶段报的错呢?

const char* p = "abcd";
*p = "123";

其实页表不仅仅只有映射关系

表这一个结构,它还附带了物理
内存中每一个区域的读写权限:

很明显,常量字符串对应的物理地址
区域的权限是只可读,所以当虚拟内存
去映射物理内存时,在页表层面发现你的
访问是权限不允许的,就会在页表层驳回
你的请求,也就是会报错!

所以页表可以起到安全评估

保护物理内存的作用!

这是页表的第一个作用

页表的第二个作用:

将无序变有序

虚拟地址在映射物理内存时
是不是随意映射的?就是说映射
到物理内存后的顺序是全乱的?
结论是当然不是!在页表层映射时
会将不同的数据类型进行划分
使得映射到物理内存后是比较
有序的一种状态!


6. 为什么要存在地址空间?

通过以下三个方面来说明

地址空间的作用:

  1. 有效的保护了物理内存

凡是非法的访问或者映射,os都
会识别到,并且终止你这个进程!
地址空间和页表都由OS创建并
进行管理,一切访问都要在OS的监管下!

  1. 使OS的耦合度更低

因为有地址空间和页表的存在,
物理内存可以不关心未来数据的
类型,可以直接对它进行加载,这样
物理内存的分配就可以和进程的管理
分开来,做到它们并没有任何关系,
所以内存管理模块和进程管理模块
就完成了解耦合的操作!

  1. 保证进程的独立性

因为有地址空间的存在,每一个进程
都认为自己拥有4GB的空间,并且各个
区域是有序的,进而可以通过页表映射
到不同的区域,来实现进程的独立性,
每一个进程不知道也不需要知道其他
进程的存在!!!


7. 总结以及拓展

关于Linux进程基础的内容就讲到

这里,下一章是全新的章节–进程控制

讲了这么久的理论了,怎么控制一个进程?

也就是创建,回收一个进程?有了前面

的基础铺垫后,后面的学习内容也就容易了

拓展阅读:

Linux中页表的内核代码


🔎 下期预告:Linux进程控制 🔍


相关文章
|
2月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
179 6
|
3月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
637 2
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
35 1
|
1月前
|
网络协议 Linux 开发工具
linux系统配置固定地址
linux系统配置固定地址
|
2月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
42 5
|
1月前
|
安全 API C#
C# 如何让程序后台进程不被Windows任务管理器强制结束
C# 如何让程序后台进程不被Windows任务管理器强制结束
63 0
|
2月前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【9月更文挑战第13天】在编程的世界中,进程间通信(IPC)如同一场精彩的社交舞会,每个进程通过优雅的IPC机制交换信息,协同工作。本文将带你探索Python中的IPC奥秘,了解它是如何让程序实现无缝信息交流的。IPC如同隐形桥梁,连接各进程,使其跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存及套接字,适用于不同场景。通过一个简单的队列示例,我们将展示如何使用`multiprocessing.Queue`实现进程间通信,使程序如同社交达人般高效互动。掌握IPC,让你的程序在编程舞台上大放异彩。
23 3
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
166 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。