『 Linux 』进程地址空间存在的意义

news2024/12/22 14:31:33

文章目录

  • 前言🦕
    • 🦖 防止进程对物理内存的非法(危险)访问
    • 🦖 进程管理模块与内存管理模块的解耦合
    • 🦖 实现进程间的独立性


前言🦕

请添加图片描述

在文章『 Linux 』进程地址空间概念中提到了进程地址空间的部分概念;

这部分概念主要围绕进程地址空间到底是什么;

在实际中,进程地址空间是一个进程的数据结构,这个数据结构的作用是模拟出虚拟地址;

当一个进程需要访问物理内存时必须经过进程地址空间获取其虚拟地址,通过页表找到页表中所映射的物理地址,才能对需要的物理地址中的数据进行操作;

在这里插入图片描述

这样的操作流程一定程度上保证了进程间与物理内存的安全性;


🦖 防止进程对物理内存的非法(危险)访问

请添加图片描述

在进程当中,每个进程都会有对应的PCB结构体(进程控制块),进程控制块与进程的进程地址空间产生对应关系;

当一个进程需要去访问对应的物理地址时将要从进程地址空间获取对应的虚拟地址,通过该虚拟地址以一种映射关系映射到对应的物理地址当中;

这个映射关系是通过一种名为页表的数据结构进行的;

页表以key/value的模型使得cpu获取到虚拟地址时能通过映射关系找到对应的物理地址;

而实际当中,页表不仅仅可以做到映射关系,页表还能做到权限查询;

当进程创建之后将会初始化进程间对应的一些数据结构,这些数据结构包括进程控制块,进程地址空间,页表等;

而在初始化页表的阶段,不仅会给页表初始化对应的虚拟地址(不一定会直接申请内存并产生映射关系),还会根据虚拟地址对应代码初始化对应的权限(页表中的页表项,不作过多说明);

使得一个进程在对物理内存进行非法访问的时候能使该进程因内权限不足不予访问;

如果进程在通过页表映射关系对物理内存的访问非法访问时则会触发页表的权限查询;

以一个例子为例,存在这样一段代码:

int main()
{
    const char *str="hello world";
    *str = 'H';
    return 0;
}

在实际中这段代码将在编译过程中的语义分析报错而导致编译失败;

假设这段代码编译未报错且生成了对应的可执行程序;

这段代码中使用*str对字符常量区进行修改;

但字符常量区的代码的权限为只读权限,即该区域内的代码不能被进行写入操作;

当进程需要对该物理内存以写入的方式进行访问时将会触发页表的权限查询行为;

在这里插入图片描述

内存管理单元(MMU)会根据页表中的映射关系讲虚拟地址转化为物理地址,并在转换的过程中进行权限检查;

如果权限不符合访问要求,MMU将会触发异常,这个异常会被传递给OS(操作系统),OS在接收到异常过后会根据异常的类型进行处理;

在OS的层面中,本质上的物理内存是不具备物理访问权限限制的,意思是物理内存本身是可以被进行任意的读写操作的;

而若是直接对物理内存进行读和写的操作时可能会出现进程间的误操作导致不能保证物理内存的安全性;

而在拥有进程地址空间(包括页表)时,在这套机制下,内存管理单元(MMU)将对页表的权限进行查询,使得若是某个进程非法对物理内存进行读写操作时能对该操作进行有效拦截;

可以使用mprotect()函数对该场景进行模拟:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>

int main() {
    const char* str = "hello world";

    // 将str所在的内存页标记为只读
    size_t page_size = sysconf(_SC_PAGESIZE);
    void* page_start = (void*)((uintptr_t)str & -page_size);
    if (mprotect(page_start, page_size, PROT_READ) != 0) {
        perror("mprotect");
        return 1;
    }
    // 尝试对只读内存区域进行写入操作
    char* writable_str = (char*)str; // 将const char*转换为char*,这是非法操作
    *writable_str = 'H'; // 这里会触发内存保护异常
    return 0;
}

在这个演示中,使用mprotect()函数将str所在的内存页标记为只读,然后尝试对只读内存区域进行写入操作;

当进程尝试对只读内存区域进行非法写入时,会触发内存保护异常,从而导致程序的终止或异常处理;

总而言之,进程地址空间的存在以及内存管理单元(MMU)对页表的权限查询机制保护了物理内存中的所有的合法数据(包括进程及与内核相关的有效数据);


🦖 进程管理模块与内存管理模块的解耦合

请添加图片描述

耦合顾名思义就是关联性:

在开发的过程中一般都要求程序的模块间尽量的低耦合高类聚;

一个程序模块间的耦合度越低,其维护成本也低;

在历史中的进程中并不存在进程地址空间,使得一个进程在访问物理地址的时候需要采用直接访问的方式(地址+偏移量);

若是采用直接对物理内存进行访问的方式对地址进行读写操作,则可能出现某些进程恶意修改其他进程的上下文内容或是其他有效代码与合法数据,将危及其他进程;

且若是采用这种方式对物理内存进行访问的话其进程管理与内存管理将是一种强耦合的关系;

对于这种强耦合的关系其维护成本必定高于弱耦合;

在这里插入图片描述

而进程地址空间的出现可以有效的将进程对内存的访问分为两个模块:

  • 内存管理模块
  • 进程管理模块

在这里插入图片描述

对于进程管理模块而言,操作系统将初始化对应的进程控制块(PCB结构体)与其内部的数据结构,这些数据结构包括进程中的进程地址空间(mm_struct),在对进程地址空间进行初始化时将对页表内部对应的虚拟地址进行初始化;

当一个进程初始化结束时(并未进入调度队列运行)时,其虚拟地址是已经通过磁盘内的虚拟地址(逻辑地址)进行同步的初始化;

但实际上其物理内存并未真正给予该进程对应的物理内存空间,只不过当该进程使用CPU资源时OS将根据进程的代码为进程合理分配物理内存;

OS为了使进程的物理地址更加具有安全性,将会采用一种ASLR的内存分布随机化的技术使得进程页表中虚拟地址所映射的物理地址进行随机分布;

即一个进程的虚拟地址在页表中所映射的物理地址在物理内存中是可以随机分布的,并不会以在语言层面的内存概念那样以栈区,堆区,正文代码区等等进行内存分布;

在这里插入图片描述

这也更加的能够使得进程管理模块内存管理模块进行解耦合;

当然在对一个进程进行初始化(包括进程地址空间)时并不一定在会将物理内存中开辟物理空间;

在语言层面当中(以c/C++为例),当使用new或者是malloc对内存申请空间时,对于上层的这个内存申请并不是实质的物理内存;

为了避免物理内存被申请时并不马上被使用所造成的空间浪费,上层在申请内存空间时本质上是在进程地址空间中申请的,当上层对进程地址空间进行内存申请时,OS并不会马上在页表中进行映射(开辟物理内存空间);

只有当真正需要对物理内存进行访问时OS才会执行内存的相关管理算法(包括内存申请与构建页表的映射关系)后再对该物理空间进行访问;

OS作为进程与各项资源的管理者,是可以随时对物理内存进行访问的,而在用户和进程的视角当中,并不会感知OS对应的执行内存相关管理算法等操作;

OS将采用一种缺页中断的技术判断页表中的虚拟地址是否有映射对应的物理地址(开辟物理空间);

总而言之,因为进程地址空间以及页表的存在,可以使得整体以内存管理模块进程管理模块两个模块进行解耦合;

在进程管理模块当中OS只需要根据磁盘中的虚拟地址(逻辑地址)对进程地址空间与页表进行初始化;

而在内存管理模块当中,OS更可以不对该进程立马分配物理地址,而是根据延迟分配的方式提高整机的效率;


🦖 实现进程间的独立性

请添加图片描述

根据上文可知,OS在通过页表中的虚拟地址映射给物理地址时(开辟空间)所采用的方式为ASLR的随内存分布随机化的方式,导致了真正在物理内存当中其物理地址的分化是无序的;

而进程地址空间和页表的存在尤其是页表的映射关系使得在进程的视角中可以使得内存有序化;

同时从上文得知,在对一个进程申请内存时其物理空间并不会马上被申请(延迟分配的策略);

由于内存管理模块与进程管理模块的解耦合,在多个进程对物理内存进行访问时OS将使用一定的内存管理使得在进程的视角当中每个进程都能够独立拥有一整块内存空间,以此实现进程间的独立性;

在这里插入图片描述

由此可知进程间的独立性可以依靠进程地址空间与页表共同完成;

总而言之由于进程地址空间的存在,在进程的视角当中每个进程都可以拥有有序的4GB内存(32位机器下);

操作系统将通过页表映射到不同的物理地址从而实现进程的独立性;

在这里插入图片描述


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

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

相关文章

设计模式—观察者模式

观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;使得当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。 在观察者模式中&#xff0c;有两个核心角色&#xf…

vue-如何实现带参数跳转页面

文/朱季谦 在vue框架的前端页面上&#xff0c;若要实现页面之间的带参数跳转&#xff0c;可参考以下实现过程&#xff1a; 例如&#xff0c;点击截图中的“查看试卷”&#xff0c;可实现带参跳转到相应的试卷页面&#xff0c;该功能类似查看试卷的详情页面&#xff1a; 假如以…

C#多线程总结

目录 前言 一、异步线程 使用async和await关键字 基于委托实现 二、同步线程 三、Thread线程 开启线程 设置线程优先级 Thread拓展封装 四、ThreadPool线程池 常规使用 设置线程数 线程等待 Thread和ThreadPool比较 通过线程池做一些扩展&#xff08;定时器类&am…

解析视频美颜SDK的算法:美肤、滤镜与实时处理

如今&#xff0c;美颜技术在视频处理中扮演着关键的角色&#xff0c;为用户提供更加精致的视觉体验。本文将深入探讨视频美颜SDK的算法&#xff0c;聚焦于美肤、滤镜与实时处理等方面&#xff0c;揭示背后的科技奥秘。 一、美肤算法的魅力 视频美颜的一个核心功能就是美肤&am…

被央视报道过的AIGC产品-贝塔创作(BetaCreator)使用指南

产品地址&#xff1a;betacreator.com 真人图 人台图 商品图 商品变色 建议使用浅色服装进行变色&#xff0c;效果更好 如果没有浅色服装&#xff0c;可以先把服装颜色变为白色

redis-学习笔记(Jedis 前置知识)

自定义的 Redis 客户端 咱们可以实现编写出一个自定义的 Redis 客户端 因为 Redis 公开了自己使用的自定义协议 ---- RESP 协议清楚了, 那么通信数据格式就清除了, 就能完成各层次之间的数据传输, 就能开发服务器和客户端 RESP — Redis 的 序列化 协议 特点: 简单好实现快读进…

App防止恶意截屏功能的方法:iOS、Android和鸿蒙系统的实现方案

防止应用被截图是一个比较常见的需求&#xff0c;主要是出于安全考虑。下面将分别为iOS&#xff08;苹果系统&#xff09;、Android&#xff08;安卓系统&#xff09;及HarmonyOS&#xff08;鸿蒙系统&#xff09;提供防止截屏的方法和示例代码。 在企业内部使用的应用中&…

python学习:浅拷贝与深拷贝详解

copy 一、 & is二、浅拷贝 & 深拷贝(一)、浅拷贝(二)、深拷贝 三、问题 一、’ ’ & ‘is’ ’ 和is是python对象比较常用的两种方式,简单来说,‘ ‘操作符比较对象之间的值是否相等,如 a b 而’is’操作符比较的是对象的身份标识是否相等,即它们是否是同一个…

HNU数据库大作业-世界杯比赛系统

前言 之前做的那个版本bug较多&#xff0c;后进行了大量优化。 此项目是一个前后端分离的项目&#xff0c;前端主要使用htmlcssjs搭建&#xff0c;使用的是layui框架 后端使用php语言&#xff0c;仅实现了简单的查询数据库功能&#xff0c;无法实现多并发查询等复杂情况 数…

jetpack compose 学习(-)

年底了,无聊的时间总是缓慢的,找个事情做一做,打发打发时间,刚好看到jetpack compose 学习学习,毕竟androidStudio 默认创建的项目都带上了这个,学习网站:https://developer.android.com/jetpack/compose/modifiers?hlzh-cn 1. 首先androidStudio创建一个新项目 喜欢kotlin的,…

亚马逊、速卖通自养号测评的安全稳定性与成本优势分析

在跨境电商平台的运营中&#xff0c;买家评价的重要性不言而喻。很多买家在购买产品前都会查看评论&#xff0c;比较同类产品的买家口碑&#xff0c;以做出更明智的购买决策。 因此&#xff0c;测评一直是各大跨境电商平台的一种重要推广方式&#xff0c;测评同时也是很多卖家…

BugKu-Web-Simple_SSTI_1Simple_SSTI_2(浅析SSTI模板注入!)

何为SSTI模块注入&#xff1f; SSTI即服务器端模板注入&#xff08;Server-Side Template Injection&#xff09;&#xff0c;是一种注入漏洞。 服务端接收了用户的恶意输入以后&#xff0c;未经任何处理就将其作为Web应用模板内容的一部分&#xff0c;模板引擎在进行目标编译渲…

TCP/IP详解——网络基本概念

文章目录 一、网络基本概念1. OSI 7层模型1.1 每层对应的协议1.2 每层涉及的设备1.2.1 物理层设备1.2.2 数据链路层设备1.2.3 网络层设备1.2.4 传输层设备1.2.5 交换机和路由器的应用1.2.6 问题 2. TCP/IP 4层模型3. 物理层传输介质3.1 冲突域 4. 数据链路层4.1 以太网帧结构4.…

socket 套接字

1、套接字介绍 socket起源于Unix&#xff0c;遵循“一切皆文件”出发点&#xff0c;都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。 在设计模式中&#xff0c;Socket把复杂的TCP/IP协议族隐藏在Socket接口后面&#xff0c;Socket去组织数据&#xf…

Godot导出Android包报错:无效的包名称

问题描述 使用Godot为项目导出Android平台包时报错&#xff0c;提示&#xff1a;“无效的包名称&#xff1a;项目名称不符合包名格式的要求。请显式指定包名。” 解决办法 修改导出配置项“包->唯一名称”。 该项缺省值“org.godotengine.$genname”不能直接使用&#x…

36V/48V转12V 10A直流降压DC-DC芯片-AH1007

AH1007是一款36V/48V转12V 10A直流降压&#xff08;DC-DC&#xff09;芯片&#xff0c;它是一种高性能的降压变换器&#xff0c;常用于工业、汽车和电子设备等领域。 AH1007采用了先进的PWM调制技术和开关电源控制算法&#xff0c;能够高效地将输入电压从36V/48V降低到12V&…

SSM整合——Springboot

1.0 概述 1.1 持久层&#xff1a; DAO层&#xff08;mapper&#xff09; DAO层&#xff1a;DAO层主要是做数据持久层的工作&#xff0c;负责与数据库进行联络的一些任务都封装在此 DAO层的设计首先是设计DAO的接口&#xff0c; 然后在spring-mapper.xml的配置文件中定义此接…

【Unity学习笔记】光照简介

本节主要是简单介绍一些常见的光照组件和渲染设置。 文章目录 灯光类型平行光Directional Light点光源Point Light聚光灯Spot Light面积光 Area Light 阴影设置全局光照明光照模式直接光照与间接光照Mixed Lighting 光照探针Light Probe Group光照探针组 反射探针 灯光类型 在…

00后女孩月薪3200,3年买两套房,这个程序员变现新风口千万要把握住

00后女孩月薪3200&#xff0c;3年买两套房&#xff0c;这个程序员变现新风口千万要把握住 前几天&#xff0c;在网上看到了一份中国90后收入的调查报告&#xff1a; 报告显示&#xff1a; 90后月均收入8000元&#xff0c;三成90后零存款&#xff0c;两成90后存款达到10万以上…

鸿蒙开发之页面与组件生命周期

一、页面间的跳转 创建文件的时候记得选择创建page文件&#xff0c;这样就可以在main->resources->profile->main_pages.json中自动形成页面对应的路由了。如果创建的时候你选择了ArkTS文件&#xff0c;那么需要手动修改main_pages.json文件中&#xff0c;添加相应的…