Linux进程学习【进程地址】

news2024/11/25 7:19:00

✨个人主页: Yohifo
🎉所属专栏: Linux学习之旅
🎊每篇一句: 图片来源
🎃操作环境: CentOS 7.6 阿里云远程服务器

  • Perseverance is not a long race; it is many short races one after another.

    • 毅力不是一场漫长的比赛;是许多短跑一个接一个。

    四姑娘山


文章目录

  • 📘前言
  • 📘正文
    • 📖问题引入
    • 📖虚拟空间划分
    • 📖真实空间分布
      • 🖋️代码实现
      • 🖋️问题反思
    • 📖进程地址空间
      • 🖋️虚拟地址
      • 🖋️页表+MMU
      • 🖋️写时拷贝
      • 🖋️内存申请
    • 📖虚拟地址空间存在的意义
  • 📘总结


📘前言

对于 C/C++ 来说,程序中的内存包括这几部分:栈区堆区静态区 等,其中各个部分功能都不相同,比如函数的栈帧位于 栈区,动态申请的空间位于 堆区,全局变量和常量位于 静态区 ,区域划分的意义是为了更好的使用和管理空间,那么 真实物理空间 也是如此划分吗?多进程运行 时,又是如何区分空间的呢?写时拷贝 机制原理是什么?本文将对这些问题进行解答

内存条:真实的物理空间,用来存储各种数据

真实物理内存条


📘正文

📖问题引入

地址是唯一的,对地址进程编号的目的是为了不冲突

这是个耳熟能详的概念,在 C语言 学习阶段,我们可以通过对变量 & 取地址的方式,查看当前变量存储空间的首地址信息

#include <stdio.h>

int main()
{
  const char* ps = "这是一个常量字符串";
  printf("字符串地址:%p\n", ps);  //%p 专门用来打印地址信息
  return 0;
}

结果

利用前面学习的 fork 函数创建子进程,使得子进程和父进程共同使用一个变量

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

int main()
{
  int val = 10;
  pid_t id = fork();
  if(id == 0)
  {
  	val *= 2;	//刻意改变共享值
    printf("我是子进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n", getpid(), getppid(), val, &val);
    exit(0);
  }

  waitpid(id, 0, 0);

  printf("我是父进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n", getpid(), getppid(), val, &val);
  return 0;
}

结果
对于同一块空间,读取到了不同的值,是不可能出现这种情况的

因为真实地址都是 唯一 的,分析:

  • 不同的空间出现同名的情况
  • 父子进程使用的真实物理空间并非同一块空间!

原因:

  • 当子进程尝试修改共享值时,发生 写时拷贝 机制
  • 语言层面的程序空间地址不是真实物理地址
  • 一般将此地址称为 虚拟地址线性地址

结论: 语言层面的地址都是虚拟地址,用户无法看到真实的物理地址,由 OS 统一管理


📖虚拟空间划分

一般用户的认知中,C/C++ 程序内存分布如下图所示,直接表示内存中的各个部分
虚拟空间


📖真实空间分布

但实际上的空间分布是这样的:
真实分布
如果有多个进程(真实地址空间只有一份),此时情况是这样的:
多进程

🖋️代码实现

在实现虚拟地址空间时,是用结构体 mm_struct 实现的

task_struct 一样,mm_struct 中也包含了很多成员,比如不同区域的边界值

//简单展示其中的成员信息
mm_struct
{
	//代码区域划分
	unsigned long code_start;
	unsigned long code_end;

	//堆区域划分
	unsigned long heap_start;
	unsigned long heap_end;

	//栈区域划分
	unsigned long stack_start;
	unsigned long stack_end;

	//还有很多其他信息
	……
}

每个进程都会有这样一个 mm_struct,其中的区域划分就是虚拟地址空间

通过对边界值的调整,可以做到不同区域的增长,如堆区、栈区扩大

mm_struct 中的信息配合 页表+MMU 在对应的真实空间中使内存(程序寻址)

🖋️问题反思

此时可以理解为什么会发生同一块空间能读取到不同值的现象了

  • 父子进程有着各自的 mm_struct,其成员起始值一致
  • 对于同一个变量,如果未改写,则两者的虚拟地址通过 页表 + MMU 转换后指向同一块空间
  • 发生改写行为,此时会在真实空间中再开辟一块空间,拷贝变量值,让其中一个进程的虚拟地址空间映射改变,这种行为称为 写时拷贝

刚开始,父子进程共同使用同一块空间
真相
当子进程修改共享值后

写时拷贝机制


📖进程地址空间

下面来好好谈谈 进程地址空间 (虚拟地址)

🖋️虚拟地址

在早期程序中,是没有虚拟地址空间的,对于数据的写入和读取,是直接在物理地址上进行的,程序与物理空间直接打交道,存在以下问题:

  • 假设存在野指针问题,此时可能直接对物理内存造成越界读写
  • 程序运行时,每次都需要大小为 4GB 的内存使用,当进程过多时,资源分配就会很紧张,引起进程阻塞,导致执行效率下降
  • 动态申请内存后,需要依次释放,影响整体效率

野指针行为

为了解决各种问题,大佬们提出了 虚拟地址空间 这个概念,有了 虚拟空间 后,当进程创建时,系统会为其分配属于自己的 虚拟空间需要使用内存时,通过 寻址 的方式,使用物理地址上的空间即可

  • 多个进程互不影响,动态使用,做到 效率资源 双赢
  • 发生越界行为时,寻址 机制会检测出是否发生越界行为,如果发生了,能在其对物理地址造成影响前进行拦截
  • 因为每个进程都有属于自己的空间,OS 在管理进程时,能够以统一的视角进行管理,效率很高

光有 虚拟地址空间 是不够的,还需要一套完整的 ‘‘翻译’’ 机制进行程序寻址,如 Linux 中的 页表 + MMU

🖋️页表+MMU

页表 本质上就是一张表,操作系统 会为每个 进程 分配一个 页表,该 页表 使用 物理地址 存储。当 进程 使用类似 malloc 等需要 映射代码或数据 的操作时,操作系统 会在随后马上 修改页表 以加入新的 物理内存。当 进程 完成退出时,内核会将相关的页表项删除掉,以便分配给新的 进程
原话出处:ARM体系架构——MMU

系统底层机制的研究是非常生涩的,这里简言之就是 页表 记录信息,通过 MMU 机制进行寻址使用内存,假设目标空间为只读区域(比如数据段、代码段),在进行空间开辟时,会打上只读权限标签。后续对这块进行写入操作时,会直接拒绝

对于这种机制感兴趣的同学可以点击下面这几篇文章查看详细内容:
Linux的虚拟内存详解(MMU、页表结构)
ARM体系架构——MMU
逻辑地址、页表、MMU等

🖋️写时拷贝

Linux 中存在一个很有意思的机制:写时拷贝
这是一种 赌bo 行为,OS 此时就赌你不会对数据进行修改,这样就可以 使多个 进程 在访问同一个数据时,指向同一块空间,当发生改写行为时,再新开辟空间进行读写

这种行为对于内置类型来说感知还不是很强,但如果是自定义类型的话,写时拷贝 行为可以在某些场景下减少 拷贝构造 函数的调用次数(尤其是 深拷贝),尽可能提高效率

可以通过一个简单的例子来证明此现象

//计算 string 类的大小
#include <iostream>
#include <string>
using namespace std;


int main()
{
	string s;
	cout << sizeof(s) << endl;
	return 0;
}

对比
原因:

  • g++ 中的 string 对象创建后,它就赌你不会直接改写,所以实际对象为一个指针类型(64位环境下为8字节),当发生改写行为时,触发 写时拷贝 机制,再进行其他操作

🖋️内存申请

值得注意的是,在进行动态内存申请时,OS 也并非直接去申请好内存,而是先判断是否有足够的内存,如果有,就在 页表 中记录相应信息(这种行为叫做 缺页中断),当程序实际使用到这块空间时,OS 才会去申请内存给程序使用

OS是一个讲究人,不允许任何空间浪费或低效率行为

假设没有 缺页中断 机制,给程序分配空间后,程序又不用,此时空间属于闲置状态,这是不被 OS 认可的低效浪费行为

缺页中断
图片来源:3.2.2 OS之请求分页管理方式(请求页表、缺页中断机构、地址变换机构)


📖虚拟地址空间存在的意义

总结一下,虚拟内存+页表+MMU 这种管理方式的好处:

  • 防止地址随意访问,保护物理内存与其他进程(权限设置)
  • 进程管理内存管理 进行 解耦,方便 OS 进行更高效的管理
  • 可以让进程以统一的视角看待自己的代码和数据

📘总结

以上就是本篇关于 Linux进程学习【进程地址】的全部内容了,我们从一个有趣的小问题切入,见识到了 虚拟地址空间物理地址空间 的奇妙关系,在种种机制的加持之下,OS 对进程的管理变得更加得心应手,系统也因此得以高效运行

如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐

Linux进程学习【环境变量】

Linux进程学习【进程状态】

Linux进程学习【基本认知】

===============

Linux工具学习之【gdb】

Linux工具学习之【git】

Linux工具学习之【gcc/g++】

Linux工具学习之【vim】

感谢支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401448.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dynabook笔记本电脑无法开机怎么重装新系统?

Dynabook笔记本电脑无法开机怎么重装新系统&#xff1f;有用户使用Dynabook笔记本电脑出现了无法正常开机的情况。遇到这样的问题是我们的电脑系统出现了损坏&#xff0c;可以尝试进行系统修复。如果无法修复的话&#xff0c;就需要进行系统重装了。以下为大家带来Dynabook笔记…

SQLMap安装教程

注意&#xff1a;在python3环境下安装sqlmap的时候会提示需要在python2的环境下才能安装&#xff0c;其实在python3.6以后也都支持sqlmap了。 sqlmap安装步骤&#xff1a; 一、下载python&#xff1b; 下载地址 https://www.python.org/downloads/ 下载教程参考&#xff08…

通过反射获取注解的属性值(内含源代码)

通过反射获取注解的属性值&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87554543 目录通过反射获取注解的属性值&#xff08;内含源代码&#xff09;源代码下载链接地址&#xff1a;[https://downl…

做互联网自媒体创业的月薪收入真的能过万吗?

搞自媒体创业有前途吗&#xff1f;收入月薪过万是真的吗&#xff1f; 自媒体创业是一种新兴的创业方法&#xff0c;它的远景十分广阔。自媒体创业能够让人们在自己的兴趣爱好和专业范畴上发挥自己的才能&#xff0c;一起也能够获得不错的收入。可是&#xff0c;月薪过万并不是…

ArangoDB——AQL编辑器

AQL 编辑器 ArangoDB 的查询语言称为 AQL。AQL与关系数据库管理系统 (RDBMS)区别在于其更像一种编程语言&#xff0c;更自然地适合无模式模型&#xff0c;并使查询语言非常强大&#xff0c;同时保持易于读写。数据建模概念 数据库是集合的集合。集合存储记录&#xff0c;称为文…

三维人脸实践:基于Face3D的人脸生成、渲染与三维重建 <三>

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 基于BFM模型&#xff0c;估计3DMM的参数&#xff0c;可以实现线性的人脸表征&#xff0c;该方法可用于基于关键点的人脸生成、位姿检测以及渲染等。推荐…

信息收集之搜索引擎

Google Hacking 也可以用百度&#xff0c;不过谷歌的搜索引擎更强大 site 功能&#xff1a;搜索指定域名的网页内容&#xff0c;可以用来搜索子域名、跟此域名相关的内容 示例&#xff1a; site:zhihu.com 搜索跟zhihu.com相关的网页“web安全” site:zhihu.com 搜索zhihu…

提升学习 Prompt 总结

NLP现有的四个阶段&#xff1a; 完全有监督机器学习完全有监督深度学习预训练&#xff1a;预训练 -> 微调 -> 预测提示学习&#xff1a;预训练 -> 提示 -> 预测 阶段1&#xff0c;word的本质是特征&#xff0c;即特征的选取、衍生、侧重上的针对性工程。 阶段2&…

C++核心编程

一、内存分区模型概述&#xff1a;C程序在执行时&#xff0c;将内存划分为4个区域程序运行前&#xff1a;代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统管理①共享。共享的目的是对于频繁被执行的程序&#xff0c;在内存中只需有一份代码即可②只读。使其只…

组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比

组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比 目录 组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 MATLAB实现EMD-KP

传输层协议 TCP UDP

目录 协议前菜 端口号 ​编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…

深度分析中国高端投教市场第一股“九方财富”的投资价值

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;九方财富&#xff08;09636&#xff09;已于3月10在港交所成功IPO上市&#xff0c;并成为了“中国在线高端投教市场第一股”。 作为中国领先的在线投资决策方案提供商&#xff0c;九方财富…

一起来学习配置Combo接口吧!

Combo接口是一个光电复用的逻辑接口&#xff0c;一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系&#xff0c;两者不能同时工作&#xff08;当激活其中一个接口时&#xff0c;另一个接口就自动处于禁用状态&#xff09;&#xf…

常用存储芯片-笔记本上固态硬盘PTS11系列推荐

在存储领域中&#xff0c;除了存储颗粒之外&#xff0c;还有一种极其重要的芯片&#xff1a;存储控制芯片。存储控制芯片是CPU与存储器之间数据交换的中介&#xff0c;决定了存储器最大容量、存取速度等多个重要参数。特别是在AI、5G、自动驾驶时代&#xff0c;对于数据处理及存…

2.HTML页面组成

html页面组成html简介-基础-元素html属性-标题-段落html链接-头部-图像html表格-列表-区块html表单-框架-颜色html字符实体-url前言&#xff1a; 在学习爬虫前&#xff0c;我们还需要了解HTML页面&#xff0c;学习它的组成部分以及各部分的意思和使用方法&#xff0c;代码我放在…

C++回顾(十八)—— 文件操作

18.1 I/O流概念和流类库结构 1 概念 程序的输入指的是从输入文件将数据传送给程序&#xff0c;程序的输出指的是从程序将数据传送给输出文件。 C输入输出包含以下三个方面的内容&#xff1a; &#xff08;1&#xff09;对系统指定的标准设备的输入和输出。即从键盘输入数据&am…

前端初学者 sdk 的使用

目录结构&#xff1a; package.json {"name": "ivanfor666","version": "1.0.0","description": "","main": "dist/index.cjs.js","module": "dist/index.esm.js",&quo…

Kafka 分区机制

Kafka 分区机制分区策略轮询策略随机策略按消息键保序策略基于地理位置的分区策略主题 (Topic) &#xff1a;承载真实数据的逻辑容器&#xff0c;主题下还分 n 个分区 Kafka 消息的三级结构&#xff1a; 主题 - 分区 - 消息主题下的每条消息只会保存在某个分区中&#xff0c;…

案例06-复用思想的接口和SQL

目录 一&#xff1a;背景介绍 二&#xff1a;思路&方案 三&#xff1a;过程 1.Controller层接口的复用 2.Mapper层sql语句的复用 四&#xff1a;总结 一&#xff1a;背景介绍 我们在开发项目的过程中非常容易出现的一种现象就是用什么我就直接写什么&#xff0c;就像我…

VPC专有网络介绍

基本概念 VPC&#xff1a;专有网络&#xff0c;云上用户自定义的私有网络。 vSwitch&#xff1a;交换机&#xff0c;组成专有网络的基础网络设备&#xff0c;交换机可以连接不同的云资源&#xff0c;在专有网络内创建云资源时&#xff0c;必须指定云资源所在连接的交换机。 vR…