当前位置: 首页 > news >正文

Linux进程——进程地址空间

前言:在讲完环境变量后,相信大家对Linux有更进一步的认识,而Linux进程概念到这也快接近尾声了,现在我们了解Linux进程中的地址空间!


在这里插入图片描述


本篇主要内容:
了解程序地址空间
理解进程地址空间
探究页表和虚拟地址空间

在这里插入图片描述


进程地址空间

  • 1. 程序地址空间
  • 2. 进程地址空间
  • 3. 什么是地址空间
  • 3. 地址空间的管理
  • 4. 页表
  • 5. 为什么要存在地址空间
  • 6. 总结拓展


1. 程序地址空间

我们在学习C语言的时候,大家都了解过这样的空间布局图

在这里插入图片描述
那么到底是不是这样排布的呢,我们来验证一下

    1 #include<stdio.h>2 #include<stdlib.h>3 4 int un_gval;5 int init_gval = 100;                         6                                      7 int main(int argc, char *argv[], char *env[])8  {                                            9      printf("code addr: %p\n", main);                   10      const char *str = "Hello, Linux!";                 11      printf("read only char addr: %p\n", str);          12      printf("init global value addr: %p\n", &init_gval);13      printf("uninit global value addr: %p\n", &un_gval);14                                     15      char *heap1=(char*)malloc(100);16      char *heap2=(char*)malloc(100);17      char *heap3=(char*)malloc(100);18      char *heap4=(char*)malloc(100);19                                        20      int a = 100;                      21                                        22      printf("heap1 addr: %p\n", heap1);23      printf("heap2 addr: %p\n", heap2);24      printf("heap3 addr: %p\n", heap3);25      printf("heap4 addr: %p\n", heap4); 26                                         27      printf("stack addr: %p\n", &str);  28      printf("stack addr: %p\n", &heap1);                                                                                                                                                       29       printf("stack addr: %p\n", &heap2);                                                                                                                                                30       printf("stack addr: %p\n", &heap3);31      printf("stack addr: %p\n", &heap4);                                                                                                                                                                  32      printf("a addr: %p\n",&a);  33      return 0;34  }

在这里插入图片描述


栈区中的数组和结构体

int num[10] ......&a[0]  &a[9]struct s
{int a; ......&s.aint b; ......&s.bint c; ......&s.c
}

在这里插入图片描述

注意:栈区是整体向下增长,局部想上使用的,就是地址最低处,依次往上放后面的元素


但是如果我们将代码更改还能运行过去嘛?

char *str = "Hello, Linux!"; 
*str = 'S';

在这里插入图片描述
显然我们是不能更改的,一更改就就运行不了了

注意:其实是因为字符常量区与代码区很接近,而编译器在编译时,字符常量区就是被编译到代码区的,代码又不可被写入,所以字符常量区也不可被修改

综上:

  • 栈区是整体向下增长,局部想上使用的,就是地址最低处,依次往上放后面的元素
  • 常量区的字符串不允许修改

但是这都是我们之前了解的知识,现在我们来重新了解地址,我们先来看这段代码

  1 #include<stdio.h>2 #include<stdlib.h>3 #include<sys/types.h>4 #include<unistd.h>5 6 int g_val = 200;7 8 int main()9 {10     pid_t id = fork();11     if(id == 0)12     {13         // 子进程14         int cnt = 5;15         while(1)16         {17             printf("child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);18             sleep(1);19             if(cnt == 0)20             {21                 g_val = 100;22                 printf("child change g_val: 200 -> 100\n");23             }24             cnt--;25         }26         27     }28     else{29         // 父进程30         while(1)31         {32             printf("father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);33             sleep(1);                                                                                                                                                                  34         }35     }36     return 0;37 }

在这里插入图片描述
我们发现在开始时,输出出来的变量值和地址是一模一样的!
因为我们之前讲过子进程按照父进程为模版,父子并没有对变量进行进行任何修改

但是在达到一定条件之后,父子进程,输出地址是一致的,但是变量内容不一样!

但是相同的地址为什么会有不同的值?

  • 所以我们能得出结论,我们之前看到的地址,绝对不是物理地址,我们平时用到的地址,其实都是虚拟地址/线性地址!
  • 而虚拟地址就是进程地址空间的内容

2. 进程地址空间

我们现在来深入的了解一下为什么相同的的地址为什么会有不同的值?

首先引入一个概念:每一个进程运行之后,都会有一个进程地址空间的存在,在系统层面都要有自己的页表映射结构!
在这里插入图片描述

因此:当一个进程先修改后,它就不再指向原来那块物理空间,而是拥有一个新的物理空间!而页表左边的虚拟空间没有发生改变,所以相同的的地址为什么会有不同的值,是因为映射的物理空间不同!


3. 什么是地址空间

在讲什么是地址空间之前,我们先来讲一个故事,来方便理解!

一个拥有10亿美元身家的富豪,他有4个私生子,每个人都不知道彼此的存在,但是富豪对每个孩子都说,认真做好现在的事,在未来可以继承自己的10个亿家产。

在这里插入图片描述

但是在得到10个亿之前,他的几个孩子,在经济上遇到了问题,前三个都要找富豪要10w美金来解决麻烦,富豪觉得合情合理也就给了,但是它的第四个孩子直接找他要10个亿,富豪当然不能给他,然后讲明原因后给了他20w美金。因此他的所有孩子都可以得到10亿之内的经济资助,但是绝对拿不到10个亿。

在这里插入图片描述

在这个故事中:

  • 操作系统:富豪
  • 内存:10亿美金
  • 进程:私生子
  • 虚拟地址空间:继承10亿的大饼

虚拟地址空间并不是真实的地址


3. 地址空间的管理

富豪给每一个私生子都画了饼,他要把每个私生子都管理起来,也就是要把所有大饼管理起来。

因此:地址空间也要被OS管理起来!!每一个进程都要有地址空间,系统中,一定要对地址空间做管理!!

而操作系统管理地址空间,一定是“先描述,在组织”!地址空间最终一定是一个内核的数据结构对象,
就是一个内核结构体!

在这里插入图片描述
而我们观察进程地址空间,发现里面是一堆的地址划分。
在Linux中,这个描述虚拟地址空间的东西叫做:

struct mm _struct
{long code_start;long code_end;long data_start;long data_end;long heap_start;long heap end; //brklong stack _start;long stack _end;......
}

在这里插入图片描述
而该结构体的大小会被初始化成4gb,线性编程范围从全0到全F,然后把线性范围拆分成细小的范围,这就是地址空间


4. 页表

在上面我们了解到了页表,页表的映射关系中左侧表示虚拟地址,右侧表示物理地址,但是除了这两个其实在页表的映射关系中还存在一个标记字段——访问权限字段

在这里插入图片描述
讲到这里我们再回到字符常量区那里。

char *str = "Hello, Linux!"; 
*str = 'S';

此时我们就可以解释通字符常量区为什么不能修改:

  • 字符常量区在经过页表映射时,访问权限字段只设置成只读的,所以在写入时,页表直接将我们拦住,不让我们访问,所以字符常量区不能修改,代码区也是如此!

所以页表可以进行安全评估,有效的进行进程访问内存的安全检查


在除去上面提到的东西以外,页表还可以通过二进制衡量能存中有没有内容,是否分配地址

在这里插入图片描述
当我们有个虚拟地址要被访问了,但是它并没有被分配空间,更不会有内容,那该则么办呢?
在这里插入图片描述

其实在这个时候操作系统会将你的这个访问暂停,然后进行一下操作:

  • 操作系统会将你的可执行程序重新开辟空间
  • 把对应可执行程序需要执行的这个虚拟地址对应的代码加载到内存里
  • 把对应的虚拟地址填充到页表
  • 把标志位改为1,代表已经分配地址,且内容已经填充
  • 将暂停的代码继续访问

操作过程也称为缺页中断

而我们操作系统在进行这些工作时,是在进行内存管理, 而进程管理和内存管理因为有了地址空间的存在 ,实现了在操作系统层面上的模块的解耦!


5. 为什么要存在地址空间

到了这里我想大家也都了解得差不多了,为什么要存在地址空间,原因有很多

一、 让无序便有序

  • 让进程以统一的视角看待内存
  • 在页表层映射时会将不同的数据类型进行划分使得映射到物理内存后是比较有序的一种状态!
  • 所以任意一个进程,可以通过地址空间+页表可以将乱序的内存数据,变成有序,分门别类的规划好!

二、存在虚拟地址空间,可以有效的进行进程访问内存的安全检查

三、将进程管理和内存管理进行解耦

四、保证进程的独立性

通过页表让进程虽然虚拟地址一样但是映射到不同的物理内存处,从而实现进程的独立性

6. 总结拓展

拓展:
在mm_struct中还会存在一个struct vm_area_struct的结构 ,它能划分出一个start,一个end。如果我们还想继续划分就会有多个struct vm_area_struct的结构,然后他们会构成一个线性划分的链表结构。

struct vm_area_struct
{struct mm_struct * vm_mm;unsigned long vm_start;unsigned long vm_end;......
}

到这里我们的进程地址空间也接近尾声了,地址空间让进程管理和内存管理互不干涉,起到了很大作用。结束进程地址空间,我们的Linux进程概念到这里也结束了,后面我将带大家走进进程控制。

谢谢大家支持本篇到这里就结束了

在这里插入图片描述

相关文章:

Linux进程——进程地址空间

前言&#xff1a;在讲完环境变量后&#xff0c;相信大家对Linux有更进一步的认识&#xff0c;而Linux进程概念到这也快接近尾声了&#xff0c;现在我们了解Linux进程中的地址空间&#xff01; 本篇主要内容&#xff1a; 了解程序地址空间 理解进程地址空间 探究页表和虚拟地址空…...

基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (三)

基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 大家继续看 https://lilianweng.github.io/posts/2023-06-23-agent/的文档内容 第二部分&#xff1a;内存 记忆的类型 记忆可以定义为用于获取、存储、保留以及随后检索信息的过程。人脑中有多…...

python3如何安装bs4

在python官网找到beautifulsoup模块的下载页面&#xff0c;点击"downloap"将该模块的安装包下载到本地。 将该安装包解压&#xff0c;然后在打开cmd&#xff0c;并通过cmd进入到该安装包解压后的文件夹目录下。 在该文件目录下输入"python install setup.py&quo…...

docker容器技术篇:rancher管理平台部署kubernetes集群

rancher管理平台部署kubernetes集群 Rancher 是一个 Kubernetes 管理工具&#xff0c;让你能在任何地方和任何提供商上部署和运行集群。 Rancher 可以创建来自 Kubernetes 托管服务提供商的集群&#xff0c;创建节点并安装 Kubernetes&#xff0c;或者导入在任何地方运行的现…...

【计算机网络原理】初识网络原理和一些名词解释​​

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…...

车载电子电器架构 —— 关于bus off汇总

车载电子电器架构 —— 关于bus off汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…...

Linux函数

目录 一、脚本函数 1.1 创建函数 1.2 使用函数 二、函数返回值 2.1 默认的退出状态码 2.2 使用return命令 2.3 使用函数输出 三、在函数中使用变量 3.1 向函数传达参数 3.2 在函数中处理变量 四、数组变量和函数 4.1 向函数中传递数组 4.2 从函数中返回数组 五、函数…...

如何查看centos7中Java在哪些路径下

在 CentOS 7 上&#xff0c;你可以通过几种方式查找安装的 Java 版本及其路径。以下是一些常用的方法&#xff1a; 1. 使用 alternatives 命令 CentOS 使用 alternatives 系统来管理同一命令的多个版本。你可以使用以下命令来查看系统上所有 Java 安装的配置&#xff1a; su…...

信息安全-古典密码学简介

目录 C. D. Shannon: 一、置换密码 二、单表代替密码 ① 加法密码 ② 乘法密码 ③密钥词组代替密码 三、多表代替密码 代数密码 四、古典密码的穷举分析 1、单表代替密码分析 五、古典密码的统计分析 1、密钥词组单表代替密码的统计分析 2、英语的统计规…...

面试题 01.05. 一次编辑

字符串有三种编辑操作:插入一个英文字符、删除一个英文字符或者替换一个英文字符。 给定两个字符串&#xff0c;编写一个函数判定它们是否只需要一次(或者零次)编辑。 示例 1: 输入: first "pale" second "ple" 输出: True示例 2: 输入: first &qu…...

针对头疼的UDP攻击如何定制有效的防护措施

分布式拒绝服务攻击&#xff08;Distributed Denial of Service&#xff09;简称DDoS&#xff0c;亦称为阻断攻击或洪水攻击&#xff0c;是目前互联网最常见的一种攻击形式。DDoS攻击通常通过来自大量受感染的计算机&#xff08;即僵尸网络&#xff09;的流量&#xff0c;对目标…...

怎么制作流程图?介绍制作方法

怎么制作流程图&#xff1f;在日常生活和工作中&#xff0c;流程图已经成为我们不可或缺的工具。无论是项目规划、流程优化&#xff0c;还是学习理解复杂系统&#xff0c;流程图都能帮助我们更直观地理解和表达信息。然而&#xff0c;很多人可能并不清楚&#xff0c;其实制作流…...

棱镜七彩参编《网络安全技术 软件供应链安全要求》国家标准发布

据全国标准信息公共服务平台消息显示&#xff0c;《网络安全技术 软件供应链安全要求》&#xff08;GB/T 43698-2024&#xff09;国家标准已于2024年4月25日正式发布&#xff0c;并将于2024年11月1日正式实施。棱镜七彩作为主要编制单位之一参与该国家标准的编制&#xff0c;为…...

Keepalived实现LVS高可用

6.1 KeepalivedLVS集群介绍 Keepalived和LVS共同构建了一个高效的负载均衡和高可用性解决方案&#xff1a;LVS作为负载均衡器&#xff0c;负责在集群中的多个服务器间分配流量&#xff0c;以其高性能和可扩展性确保应用程序能够处理大量的并发请求&#xff1b;而Keepalived则作…...

【力扣】1089.复写零

原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不…...

Golang实践录:gin框架使用自定义日志模块

本文介绍在 Golang 的 gin 框架中使用自定义日志模块的一些方法。 背景 很早之前就实现并使用了自己封装的日志模块&#xff0c;但一直没有将gin框架内部的日志和日志模块结合。gin的日志都是在终端上打印的&#xff0c;排查问题不方便。趁五一假期&#xff0c;集中研究把此事…...

Django之配置数据库

一&#xff0c;创建项目 二&#xff0c;将项目的setting.py中的 DATABASES {default: {ENGINE: django.db.backends.sqlite3,NAME: BASE_DIR / db.sqlite3,} }替换成如下&#xff08;以mysql为例&#xff09; DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: …...

Ajax 笔记02

01 jq中的ajax方法中的dataType属性 dataType属性的属性值有以下几种: xml 返回数据按照xml解析 json 返回的数据按照json代码解析 script 返回的数据按照js代码解析 text 把返回的数据按照普通文本解析 jsonp 跨域 json: javascript object notation(js对象简谱) json整体…...

【隧道篇 / WAN优化】(7.4) ❀ 03. WAN优化的原理 ❀ FortiGate 防火墙

【简介】相信对WAN优化感兴趣的人都会有疑问&#xff0c;WAN优化真的有作用吗&#xff1f;如果真的有作用&#xff0c;那是根据什么原理呢&#xff1f;让我们来更深入的了解一下。 客户端和服务器端 其实很多人在一开始看到WAN优化这个词&#xff0c;就自然的以为上网速度太慢&…...

网络爬虫概述与原理

网络爬虫概述与原理 网络爬虫简介狭义上理解功能上理解常见用途总结 网络爬虫分类通用网络爬虫聚焦网络爬虫增量网络爬虫深度网络爬虫 网络爬虫流程网络爬虫采集策略深度有限搜索策略广度优先搜索策略 网络爬虫简介 通过有效地获取网络资源的方式&#xff0c;便是网络爬虫。网…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...