【Linux】搞懂进程地址空间

news2025/1/22 20:47:41

文章目录

    • 1、从程序地址空间开始
      • 1.1 在C/C++中看到的地址实际是?
      • 1.2 感性理解虚拟地址空间
    • 2、进程地址空间
      • 2.1 进程地址空间是怎样被描述的?
      • 2.2 进程地址空间和内存的关系
      • 2.3 为什么需要进程地址空间?

1、从程序地址空间开始

1.1 在C/C++中看到的地址实际是?

在之前,我们学习C/C++的时候看过一个地址空间,它是这样的。
我们带着疑问,这个所谓的内存区域实际是计算机的物理内存吗?
在这里插入图片描述
我们在Linux中,通过一个程序验证一下。
首先fork被调用后创建子进程,操作系统为了管理一个进程,在父进程创建子进程时,必须拷贝父进程的数据结构(子进程按照父进程为模板)。

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int g_val = 0;
  5 
  6 int main()
  7 {
  8     pid_t id = fork();
  9     if(id < 0)
 10     {
 11         perror("fork fail\n");
 12         return 0;
 13     }
 14     else if(id == 0)
 15     {
 16         //子进程
 17         printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
 18     }                                                                                    
 19     else                                             
 20     {                                     
 21         //父进程     
 22         printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 23     }              
 24     sleep(1);                                                                                                                                       
 25                                         
 26     return 0;          
 27 }

结果显示在同一个地址空间。
在这里插入图片描述
修改一下程序
我们并不能确定父子进程哪个先跑完,所以给父进程代码sleep3秒,让子进程修改g_val后先跑完再执行父进程。

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int g_val = 0;
  5 
  6 int main()
  7 {
  8     pid_t id = fork();
  9     if(id < 0)
 10     {
 11         perror("fork fail\n");
 12         return 0;
 13     }
 14     else if(id == 0)
 15     {
 16         //子进程
 17         g_val = 100;
 18         printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);                         
 19     }                                                
 20     else                                  
 21     {              
 22         //父进程                              
 23         sleep(3);                                                                                                                                   
 24         printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 25     }                                   
 26     sleep(1);          
 27 
 28     return 0;                                                      
 29 }

在这里插入图片描述

从结果上看,由于进程之间是独立的,所以在子进程修改g_val后,父进程的值依然不变,这很正常,但是竟然出现了同一个空间出现两个不同的值。

这是我们后续需要解释的一个现象。
不过在此前,我们需要认识到,物理内存是不可能一个地址存两个不同的值的,所以上述的地址空间不是在内存中,其实我们上述所指的地址空间其实是一个虚拟地址空间

1.2 感性理解虚拟地址空间

虚拟地址空间其实就是让程序自己认为独占内存空间。(其实不是)

通过一个故事理解。
操作系统比喻成一个富老爸,这个老爸假设有10亿资产(对应内存空间),而这个富老爸有3个私生子(3个进程),他们彼此并不知道对方的存在。

每个进程以为自己独占空间,其实没有。
富老爸为了让每个私生子做好自己的事,给每个人都偷偷的画了一个饼:“ 只要你努力变得有出息,在我死后我就把我的10亿资产都继承给你 ”(虚拟地址空间)。

进程向内存申请空间,但如果太多会被操作系统拒绝
有个儿子可能在自己努力的道路上遇到点挫折,需要在老爸那里借点钱(进程向内存申请空间),但是这个钱(内存空间)不会很多,如果很多的话,老爸(操作系统)会拒绝的。


虚拟地址空间是由操作系统画的一个饼,那么操作系统是如何画的呢?

公司给员工画饼,员工需要记忆好,不然画饼没有意义。
画饼本质:在你大脑里构建一副蓝图 – 数据结构struct 蓝图

如果公司给400个员工每个画了一个饼,员工需要被管理,每个员工对应的饼也要管理,不然认错了。
如果操作系统给400个进程每个构建了一个虚拟地址空间,进程需要被管理,每个对应的虚拟空间也要被管理。
那么就需要对每个虚拟空间进行描述,创建对应结构体,再利用数据结构组织起来。
地址空间的本质:是内核的一种数据结构!struct mm_struct。

所以我们知道了,在操作系统中,进程地址空间就是一个虚拟的空间,那么我们如何描述它呢?

2、进程地址空间

2.1 进程地址空间是怎样被描述的?

在此之前,我们需要对地址空间有一个基础。
下面对32位进行讨论

  1. 地址空间描述的基本空间大小是字节。
  2. 32位下 -> 2^32个地址(00 00 00 00 -> ff ff ff ff)。
  3. 内存空间2^32个字节 就是 4GB空间。
  4. 每个字节都要有唯一的地址。(地址最大作用就是保证唯一性)

在这里插入图片描述
(1个字节是每个地址对应空间大小,地址4个字节是指自身大小)

  • 描述进程地址空间

操作系统会为每个进程创建一个虚拟的进程地址空间,对应的进程PCB内会有一个指针(struct mm_struct *mm;)指向这个空间,而这个空间的描述在内核代码中就是一个结构体(struct mm_struct { … })。

进程地址空间通过对应的结构体进行描述,再通过相应的数据结构进行管理。
每个进程对应的进程地址空间,不是一开始就给4GB空间的,而是通过数据结构修改结构体变量进行调整空间大小。
在这里插入图片描述

Linux内核部分代码:
对应PCB,在进程各种属性中,有一个mm指针管理着内存信息。
在mm_struct结构体中,有着维护各种区域字段的属性信息。

struct task_struct {
	...
	struct mm_struct *mm; //进程内存管理信息
	...
}

struct mm_struct {
	...
	unsigned long total_vm, locked_vm, shared_vm, exec_vm;
	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
	unsigned long start_code, end_code, start_data, end_data;     /*维护代码区和数据区的字段*/
	unsigned long start_brk, brk, start_stack;       /*维护堆区和栈区的字段*/
	unsigned long arg_start, arg_end, env_start, env_end;  /*命令行参数的起始地址和尾地址,环境变量的起始地址和尾地址*/
	...
}

所以我们知道了,所谓进程地址空间,其实就是一个进程PCB属性指向的一个数据结构,通过相应数据结构操作调整这个结构体变量就可以调整对应虚拟空间大小。

但是,程序是要加载到内存的,进程地址空间和内存有什么关系呢?

2.2 进程地址空间和内存的关系

程序加载到内存,操作系统会为每一个程序创建匹配的进程控制块并管理起来,每一个进程控制块指向加载到内存的程序,而在这个指向中,进程地址空间是虚拟地址,虚拟地址是如何与内存的物理地址打交道的呢?

  • 页表的使用

当一个程序将代码和数据加载到内存,操作系统创建对应的PCB同时为进程创建进程地址空间。

当有一个int a = 10;加载到内存中,对应进程地址空间就有一个虚拟地址,内存中也有一个物理地址。

操作系统中进程自己的页表,将对应的虚拟地址和物理地址保存并匹配起来,这样就能使得虚拟地址通过映射访问到物理空间。
当我们需要修改一个数据(比如将a = 10 改为 a = 100时),我们看到的&a是虚拟地址,就能直接通过页表访问物理空间将a修改为100。
在这里插入图片描述
这里粗略的了解一下页表的功能,以上所有工作都是由操作系统做的

必须认识到,进程地址空间只是一个“饼”,实际上根本没有4GB,操作系统按需求分配给进程空间,其实也不会分配很多。
拓展知识:

  1. 内存被使用时基本单位是4KB。
  2. 一个4KB空间称为页。
  3. 进程地址空间由于地址都是连续的,所以也被称作线性地址。
  4. 虚拟地址空间包括进程地址空间和页表

2.3 为什么需要进程地址空间?

  • 为了数据安全
    如果让进程之间访问物理内存,要是出现越界访问呢?

    越界访问可能使得一些重要数据被篡改,非常不安全,但这只能证明直接访问物理内存不行。

    所有进程的虚拟地址空间中的页表不是只做映射,页表会根据请求的合理进行判断,不合理的请求会拒绝。

  • 保证进程独立性
    在之前,我们看到同一个虚拟空间存储着两个不同的值这个现象,接下来我们来探讨这个现象。
    在这里插入图片描述

fork调用之后,父子进程共享后续代码,当子进程对共享空间修改时,操作系统先会对原空间进行拷贝,再修改子进程对应的页表映射,最后再让子进程修改数据。

操作系统为了保证进程的独立性,做了很多工作。通过进程地址空间,通过页表,让不同进程映射到不同物理内存。

这里是引用
进程 = 内核数据结构(PCB) + 加载到内存的代码和数据。
进程地址空间和页表体现了内核数据结构的独立性,写时拷贝也体现了数据的独立性,所以进程具有独立性。

  • 方便直接使用编译器的编码地址(了解)

这里是引用

拓展知识:
逻辑地址有两种构建方式:
1、用32位线性编址方式。
2、从0开始,通过区域起始地址+偏移量方式编址,加载到物理内存后将物理空间起始地址+偏移量方式编址。

本章完~

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

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

相关文章

并发:线程状态

java的线程状态分为六种 新建 NEW 当一个线程对象被创建&#xff0c;但是还没有调用start方法时处于新建状态 此时未与操作系统底层线程关联 可运行 RUNNABLE 调用了start方法&#xff0c;就会由新建进入可运行状态 此时与底层线程关联&#xff0c;由操作系统调度执行 &…

python就是学不会怎么办?

编程从来都不是只看&#xff0c;只照葫芦画瓢就能学会的学科要想学会&#xff0c;必须是要有你个人是思考的&#xff0c;学会掌握编程逻辑&#xff0c;在学的过程中想为什么这么写&#xff0c;应该怎么去实现这个功能&#xff0c;拆分为几步不断的实操练习才能让你真的掌握知识…

并发,并行,串行,同步,异步,进程,进程池,线程,线程池

并发&#xff0c;并行&#xff0c;串行&#xff0c;同步&#xff0c;异步&#xff0c;进程&#xff0c;进程池&#xff0c;线程&#xff0c;线程池 进程 什么是进程&#xff1a; 开发写的代码称之为程序&#xff0c;将程序运行起来&#xff0c;就是进程 进程是申请一块内存空…

vue中动态引入图片为什么要是require, 你不知道的那些事

相信用过vue的小伙伴,肯定被面试官问过这样一个问题:在vue中动态的引入图片为什么要使用require 有些小伙伴,可能会轻蔑一笑:呵,就这,因为动态添加src被当做静态资源处理了,没有进行编译,所以要加上require, 我倒着都能背出来… emmm… 乍一看好像说的很有道理啊,但…

Linux实现文件定期本地备份/异地备份/删除备份的脚本

一.背景 1.总会出出现环境上的数据丢失&#xff0c;在没有备份的情况下会非常的被动&#xff0c;不管是由于病毒还是人为的原因造成的程序、数据被删除&#xff0c;有时候后悔已经来不及&#xff0c;不如提前做到数据的备份&#xff0c;而异地备份也更加的保险一点。 2.数据备…

【Servlet】1:踏入JavaWeb的第一把钥匙

目录 第一章 | JavaWeb序章 | 章节概述 | JavaWeb项目运行流程 | 静态与动态资源 | Javaweb开发人员的三个任务 第二章 | HTTP网络协议 | 章节概述 | HTTP简介 | HTTP请求三要素 | HTTP请求包、响应包 | HTTP状态码 | HTTPS vs HTTP 本文章属于后端全套笔记的第三部…

Jenkins 如何 使用企业权限 登录

实现方式 基于Jenkins的 “CAS Plugin” 和 “Role-based Authorization Strategy”插件实现 CAS Plugin: 该插件支持使用CAS作为身份验证源&#xff0c;以支持单点登录(SSO)。一般的&#xff0c;企业都会有 这么一个服务&#xff0c;提供企业 统一身份管理&#xff0c;比如 百…

【GlobalMapper精品教程】026:影像黑边白边出现的原因及解决办法汇总

本文讲解Globalmapper中,影像黑边白边出现的原因及解决办法汇总。 文章目录 1. 影像黑边白边出现的原因2. 影像黑边白边的解决方法1. 影像黑边白边出现的原因 通常,由于影像格式、像素深度、无效值、背景值等原因,会产生黑边或者白边,给影像的拼接或者裁剪带来困扰。 2. …

基于ERP集成的流程制造管理系统

ERP的含义是企业资源计划&#xff0c;含义是对企业所拥有各种资源进行综合规划和优化管理&#xff0c;用以降低成本&#xff0c;提高效率&#xff0c;增加利润。ERP最初是在90年代初提出的&#xff0c;那时的ERP概念&#xff0c;还只是根据计算机技术的发展和供需链管理,推论各…

什么是GEMM?该怎么去学习GEMM?

什么是GEMM&#xff1f; 它的英文全称是 GEneral Matrix to Matrix Multiplication (通用矩阵的矩阵乘法)&#xff0c;Gemm在神经网络的计算中占据很重要的位置。 它代表全局矩阵到矩阵的乘法&#xff0c;它本质上完全按照它在tins上所说的那样&#xff0c;将两个输入矩阵乘法在…

批量替换网站程序中的gotoip域名

网站访问出现验证页面,这是因为网站调用了二级域名&#xff0c;西部数码二级域名用于测试访问效果&#xff0c;不能用于正式访问&#xff0c;但是有些用户网站调试安装时使用了二级域名&#xff0c;内部链接没有修改&#xff0c;所以访问调用出现了验证页面。 这时要检查网站后…

DBCO(二苯并环辛炔)DBCO-PEG16-Benzylamine特点分享

【产品描述】 DBCO-PEG16-Benzylamine固体状&#xff0c;长链小分子PEG点击试剂&#xff0c;DBCO&#xff08;二苯并环辛炔&#xff09;是一种环炔烃&#xff0c;可以通过在水溶液中通过应变促进的1,3-偶极环加成反应与叠氮化物反应&#xff0c;这种生物正交反应也称为无铜点击…

前端、vue、Vue3弹幕实现;前端CSS实现弹幕

前端基于CSS3实现弹幕 基于CSS3动画 根据 Google Developer&#xff0c;渲染线程分为 主线程 (main thread) 和 合成线程 (compositor thread)。如果 CSS 动画只是改变 transforms 和 opacity&#xff0c;这时整个 CSS 动画得以在 合成线程 完成&#xff08;而JS动画则会在 主…

PTA题目 最佳情侣身高差

专家通过多组情侣研究数据发现&#xff0c;最佳的情侣身高差遵循着一个公式&#xff1a;&#xff08;女方的身高&#xff09;1.09 &#xff08;男方的身高&#xff09;。如果符合&#xff0c;你俩的身高差不管是牵手、拥抱、接吻&#xff0c;都是最和谐的差度。 下面就请你写个…

HTTP Only限制XSS盗取cookie

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是HTTP Only限制XSS盗取cookie。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对…

使用 Helm Cli 将 chart 推送到 Harbor

使用 Helm Cli 将 chart 推送到 Harbor 背景问题 努力寻找适用于特定版本的 Harbor 和 Helm 的文档。 我尝试添加我的仓库&#xff08;repo&#xff09; helm repo add harbor https://myharbor.mydomain.com/chartrepo/myproject --username myusername --password mypass…

NXP iMX8M Plus M7核心FreeRTOS开发

By Toradex胡珊逢 Toradex 的 Verdin iMX8M Plus 计算机模块采用 NXP 的 iMX8M Plus 处理器。该 CPU 除了有支持 AI 硬件加速单元的 NPU 外&#xff0c;还配置了一个 M7 微控制器。相比于 iMX 8 QuadMax 处理器上时钟频率为 266MHz 的M4 微控制器&#xff0c; 该M7 的时钟频率…

Java基础39 Object类(节选)

ObjectObject类一、 equals( )● 与 equals的对比&#xff08;☆&#xff09;二、hashCode( )三、toString( )四、finalize( )Object类 Object类&#xff1a;Java中lang包的类&#xff0c;是类层次结构的根类&#xff0c;每个类都使用Object作为超类。所有对象&#xff08;包…

【2015NOIP普及组】T3:求和 试题解析

【15NOIP普及组】求和 时间限制: 1000 ms 内存限制: 131072 KB 【题目描述】 一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。每个格子上都染了一种颜色colori用[1,m]当中的一个整数表示),并且写了一个数字numberi。 定义一种特殊的三元组:(x,y,z),其中x,…

Java架构该如何进阶?还在东拼西凑的学习?这份进阶指南相信会对你有所帮助,十多位资深大佬独家秘籍一并传授!

如何成为一名架构师? 笔者认为,想成为一名架构师&#xff0c;首先第一点必然是你的技术足够优秀&#xff0c;知识的深度和广度足够&#xff0c;遇到问题能很快从脑海中寻找出最合适的解决之道。其次,架构师会从整体上领导项目&#xff0c;与人打交道必不可少&#xff0c;因此…