目录

Ⅰ、什么是程序地址空间?

Ⅱ、虚拟地址空间是什么样的?

一、虚拟地址空间和页表

1、什么是页表?

2、什么是虚拟地址空间?

3、什么是vm_area_struct?

Ⅲ、为什么要用虚拟地址空间?

一、进程的独立性

二、保护数据安全

三、管理模块和内存模块的解耦合


Ⅰ、什么是程序地址空间?

在我们之前学习C语言/C++的时候可能我们看过下面图片,当时有人告诉我们不同的数据和代码存放在了内存的什么地方,当我们现在学习了操作系统了以后我们对这个图片产生了一些疑问,我们我们在之前的博客说过进程的产生如果我们的内存是这样排布的我们的进程应该怎么去存放?现在我们将提出虚拟地址空间也叫程序地址空间。

那什么是虚拟地址空间?

说的形象一点,假设操作系统是老板,进程是员工,那么虚拟地址空间就是老板给员工画的大饼,操作系统告诉进程,这个内存里边只有你和我在这里边,你只要好好进行这里边的内存全部是你的。这样进程以为他可以拥有全部的内存。这就是虚拟地址空间。

下面我们来看一段代码:

#include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 5 6 int num = 0;7 8 int main()9 {10     pid_t id = fork();11     if(id == 0)12     {13         //子进程14         num = 1;15         printf("我是一个子进程,num地址:%p, num = %d \n", &num , num);                                                                                                         16     }17     else18     {19 20         printf("我是一个父进程,num地址:%p, num = %d \n", &num , num);21     }22     return 0;23 }
~                                                                                                                                                                                   
~                                                                      

看上面这段代码我们可以发现在同一个地址下不同的进程的值竟然是不一样的,我们在之前的学习我们知道一个地址只能有一个值,这是为什么?这其实就是虚拟地址空间的作用下面我们来详细了解虚拟地址空间。

Ⅱ、虚拟地址空间是什么样的?

一、虚拟地址空间和页表

我们先来解释一下上文的问题,为什么一个地址父子进程的值却不一样?这时以为在操作系统创建进程的时候不仅仅是创建PCB,去创建虚拟地址空间和页表,还要去加载代码和数据。这样一个进程才真正的被创建。

1、什么是页表?

下面我们先来解释页表的功能:页表可以把虚拟地址一一对应真实的物理地址。

我们上面说过虚拟程序地址就像操作系统给进程画的一张大饼,操作系统告诉进程你可以把你的数据按照一定的顺序放,进程把自己的代码和数据按照不同的特性放在了不同的区域,但实际上内存中不止一个进程当然一个进程也不可能拥有一整块内存,所以操作系统要靠一个东西把进程的代码和数据在虚拟地址空间和物理空间一一对应这个东西就是页表。

2、什么是虚拟地址空间?

虚拟地址空间被分为了很多部分,比如堆区,栈区,等等。那么如何去做区域划分的呢?我们知道整个虚拟地址空间(假设是32位机器)被统一编址从0X00000000~0Xffffffff。如果我们可以记录每个区域的起始和结束那么就可以实现了区域划分,那么在操作系统内核中是如何做的呢?在Linux中用了一个内核数据结构来实现这个就是mm_struct。

struct mm_struct
{
struct vm_area_struct *mmap; /* 指向虚拟区间(VMA)链表 */
struct rb_root mm_rb; /* red_black树 */
unsigned long task_size; /*具有该结构体的进程的虚拟地址空间的⼤⼩*/
/*...*/
// 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。
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;
}

3、什么是vm_area_struct?

我们知道在我们申请空间的时候是在堆上开空间,那么我们每次申请的空间都是不连续的那么mm_struct里只有两个变量去区分堆的开始和结束,这里我们就有一个疑问:我们开的空间是不连续的但我们只有两个变量去确定区间,也不能把每个空间的开始和结束都表示出来啊?

这个时候我们用vm_area_struct去解决这个问题

struct vm_area_struct {
unsigned long vm_start; //虚存区起始
unsigned long vm_end; //虚存区结束
struct vm_area_struct *vm_next, *vm_prev; //前后指针
struct rb_node vm_rb; //红⿊树中的位置
unsigned long rb_subtree_gap;
struct mm_struct *vm_mm; //所属的 mm_struct
pgprot_t vm_page_prot;
unsigned long vm_flags; //标志位
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
const struct vm_operations_struct *vm_ops; //vma对应的实际操作
unsigned long vm_pgoff; //⽂件映射偏移量
struct file * vm_file; //映射的⽂件
void * vm_private_data; //私有数据
atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

我们看上面的代码我们可以看见start,end,next,prev,其实mm_struct中只记录每个区域总的开始和结束而具体的每个空间(比如我们在堆上开辟的空间)的 开始和结束是由vm_area_struct记录,然后将vm_area_struct再组织起来。

组织虚拟空间有两种方式:

1、当虚拟区比较少的时候用单链表,mmap指向这个链表

2、当虚拟区比较多的用红黑树进行组织,mm_rb指向这颗红黑树

Ⅲ、为什么要用虚拟地址空间?

一、进程的独立性

每个进程都有一个task_struct , mm_struct , vm_area_struct等相互独立,页表将虚拟地址和物理地址一一对应的,当创建子进程被创建的时候操作系统会先拷贝父进程的task_struct , mm_struct , vm_area_struct然后将一部分,比如pid,ppid等改变,最后交给子进程,当父进程或者子进程对数据进行改变的时候操作系统会进行写时拷贝,重新开辟一个新的物理空间然后把数据拷贝进去再去改动,然后操作系统改变子进程的页表中这个变量对应的物理地址而虚拟地址不改变,这也就解释了为什么一个地址会有两个值的问题。如果没有虚拟地址空间就没有页表也就不能实现独立性。

二、保护数据安全

页表是由操作系统去维护的如果想要去访问一个不合法的地址或者是数据的话当查询页表的时候操作系统就可以去拒绝进而对数据形成了保护。

三、管理模块和内存模块的解耦合

task_struct,mm_struct等这是属于管理模块,物理内存等都是属于内存模块,以为有虚拟地址空间和页表才会实现无论数据在物理内存是如何存储的在虚拟内存空间都是有序的,这样两个模块就可以解耦合。阻塞挂起也可以很好的展示管理模块和内存模块解耦合的好处,当进程是挂起状态的时候,操作系统会把进程的代码和数据换出到磁盘中,然后把页表的物理地址删除当进程调用数据和代码的时候操作系统再换回到内存中,这也很好展示了管理和内存模块是解耦合的。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【iOS】消息传递和消息转发

文章目录前言一、消息传递&#xff1a;objc_msgSend 的“查字典递归找家长”流程1. 第一步&#xff1a;查“最近调用记录”&#xff08;方法缓存&#xff09;—— 最快即快速查找&#xff01;2. 第二步&#xff1a;翻“自己的字典”&#xff08;类方法列表查找&#xff09;——…

MySQL查询优化与事务实战指南

本节用到的员工信息管理表结构放到资源中&#xff0c;需要的同学自取。本节内容以此表为示例&#xff1a; 面试题&#xff1a;innodb与myisam的区别。 外键&#xff0c;事务 特性InnoDBMyISAM事务支持支持不支持外键支持不支持锁粒度行级锁表级锁索引结构聚簇索引非聚簇索引崩…

Windows 10/11 磁盘清理操作指南:彻底解决系统盘空间不足问题

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#,Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发…

b-up:Enzo_Mi:深度学习基础知识

1.最近邻差值&#xff08;Neareast Neighbor Interpolation&#xff09; 插值算法 &#xff5c; 最近邻插值法_哔哩哔哩_bilibili 上图中最后一行&#xff0c;第一个图像&#xff0c;因为目标像素&#xff08;放大后&#xff0c;位于第1行第0列的像素&#xff09;距离它最近的…

微信小程序商品结算功能

整体结算流程概述微信小程序的商品结算涉及前端交互、API调用和数据管理。典型流程包括&#xff1a;用户交互&#xff1a;用户选择商品、填写地址和时间。数据获取&#xff1a;从小程序缓存或后端服务器获取订单信息。逻辑处理&#xff1a;验证参数、应用红包折扣。提交订单&am…

2025年7月份最新一区算法——向光生长算法

注&#xff1a;该算法已按照智能优化算法APP标准格式进行整改&#xff0c;可直接集成到APP中&#xff0c;方便大家与自己的算法进行对比。&#xff08;近期智能优化算法APP将会迎来超级大更新&#xff01;请时刻保持关注哦&#xff01;&#xff09;向光生长算法&#xff08;Pho…

脚手架新建Vue2/Vue3项目时,项目文件内容的区别

一. package.json vue版本号不同vue2中会多一个依赖&#xff1a;vue-template-compiler&#xff0c;作用是预编译Vue2模板为渲染函数&#xff0c;减少运行时开销。vue-template-compiler与vue版本要保持一致&#xff0c;否则会报错。eslintConfig中的extends不同 eslintConfig…

微信小程序入门实例_____从零开始 开发一个每天记账的微信小程序

在前面的微信小程序实例中我们开发了体重记录等实用小程序&#xff0c;今天来尝试一个和生活消费紧密相关的 ——“每日记账小程序”。它能帮你随时记录收支情况&#xff0c;让每一笔花费都清晰可查。下面就跟着步骤&#xff0c;一步步构建这个小程序。​体验一个开发者的快乐。…

2026python实战——如何利用海外代理ip爬取海外数据

家人们&#xff01;随着跨境电商的发展&#xff0c;是不是越来越多的小伙伴们也开始搞海外的数据分析了&#xff1f;不过虽然我们已经整天爬虫、数据采集打交道了&#xff0c;但一到海外数据&#xff0c;还是有不少人掉进坑里。你们是不是也遇到过以下情况&#xff1a;花了一堆…

Spring Boot启动原理:从main方法到内嵌Tomcat的全过程

Spring Boot的启动过程是一个精心设计的自动化流程&#xff0c;下面我将详细阐述从main方法开始到内嵌Tomcat启动的全过程。 1. 入口&#xff1a;main方法 一切始于一个简单的main方法&#xff1a; SpringBootApplication public class MyApplication {public static void m…

小白学Python,网络爬虫篇(1)——requests库

目录 一、网络爬虫的介绍 1.网络爬虫库 2.robots.txt 规则 二、requests 库和网页源代码 1.requests 库的安装 2.网页源代码 三、获取网页资源 1.get () 函数 &#xff08;1&#xff09;get() 搜索信息 &#xff08;2&#xff09;get() 添加信息 2.返回 Response 对象…

平板可以用来办公吗?从文档处理到创意创作的全面测评

在快节奏的现代职场&#xff0c;一个核心疑问始终萦绕在追求效率的职场人心中&#xff1a;平板电脑&#xff0c;这个轻薄便携的设备&#xff0c;真的能替代笔记本电脑&#xff0c;成为值得信赖的办公伙伴吗&#xff1f; 答案并非简单的“是”或“否”&#xff0c;而是一个充满潜…

docker gitlab 备份 恢复 版本升级(16.1.1到18.2.0)

docker 启动 # 在线 docker pull gitlab/gitlab-ce:latest # 离线 docker save -o gitlab-ce-latest.tar gitlab/gitlab-ce:latest docker load -i gitlab-ce-latest.tardocker run --detach \--publish 8021:80 --publish 8023:22 \ --name gitlab_test \--restart always \-…

web3 区块链技术与用

#53 敲点算法题 瑞吉外卖day4 调整心态 睡眠 及精神 web3 以下是应北京大学肖臻老师《区块链技术与用》公开课的完整教学大纲&#xff0c;综合课程内容、技术模块及前沿扩展&#xff0c;分为核心章节与专题拓展两部分&#xff0c;引用自公开课资料及学员笔记。 &#x1f4…

Redis1:高并发与微服务中的键值存储利器

redis中存储的数据格式为键值对&#xff08;Key,Value&#xff09;在高并发的项目和微服务的项目会频繁的用到redisNoSQL型数据库1.初始Redis1.1认识NoSQLSQL&#xff1a;structure query language关系型数据库结构化&#xff1a;有固定格式要求&#xff08;表关系&#xff0c;…

/字符串/

字符串 个人模板 5. 最长回文子串 93. 复原 IP 地址 43. 字符串相乘 227. 基本计算器 II

我的开发日志:随机数小程序

文章目录前言UI设计代码前言 为什么我要设计这个程序呢&#xff1f;因为我要用&#xff0c;懒得在网上下载了&#xff0c;于是干脆写了一个。 UI设计 UI是我凹出来的&#xff0c;你们要使用&#xff0c;直接新建一个UI.ui文件&#xff0c;然后把下面的东西输进去就可以了。 …

《Oracle SQL:使用 RTRIM 和 TO_CHAR 函数格式化数字并移除多余小数点》

select RTRIM(to_char(1222.11123344,fm9999990.9999),.) from dual 这条 SQL 语句主要用于对数字进行格式化处理&#xff0c;并移除格式化结果右侧多余的小数点。下面将详细拆解该语句的执行过程和各部分作用。语句详细拆解1. to_char(1222.11123344,fm9999990.9999)函数功能&…

「Java案例」方法重装求不同类型数的立方

利用方法重装实现不同类型数值的立方计算 立方计算方法的重载实现 编写一个程序,要求编写重载方法xxx cube(xxx value)实现对不同类型数值计算立方。 # 源文件保存为“CubeCalculator.java” public class CubeCalculator {public static void main(String[] args) {// 测试…

API 接口开发与接入实践:自动化采集淘宝商品数据

在电商数据分析、价格监控等场景中&#xff0c;自动化采集淘宝商品数据具有重要价值。本文将详细介绍如何通过 API 接口开发实现淘宝商品数据的自动化采集&#xff0c;包含完整的技术方案和代码实现。 一、淘宝 API 接入基础 1. 接入流程概述 注册淘宝账号获取 ApiKey 和 Ap…