初识进程的地址空间、页表

一、🌟问题引入

🚩代码一:

   #include<stdio.h>#include<unistd.h>int g_val=100;int main(){pid_t id=fork();if(id==0){//子进程while(1){printf("I am a child  pid:%d ppid:%d  g_val:%d\n",getpid(),getppid(),g_val);sleep(1);}}else{while(1){printf("I am a parent  pid:%d ppid:%d  g_val:%d\n",getpid(),getppid(),g_val);sleep(1);}}return 0;                                                                                                                                                                                  }

可以看出子进程与父进程中g_val的地址值是一样的,这是因为子进程继承了父进程的代码和数据,两者共享数据

🚩代码二:(子进程修改数据)

   #include<stdio.h>#include<unistd.h>int g_val=100;int main(){pid_t id=fork();if(id==0){//子进程int cnt=5;while(cnt){printf("I am a child  pid:%d ppid:%d  g_val:%d &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);cnt--;}if(cnt==0){g_val=0;printf("g_val changed: g_val:%d &g_val:%p\n",g_val,&g_val);while(1){printf("I am a child  pid:%d ppid:%d  g_val:%d &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}}                                                                                                                                                                                            else{while(1){printf("I am a parent  pid:%d ppid:%d  g_val:%d &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}return 0;}

我们发现,父进程与子进程中g_val的值不一样,这可以理解,因为进程之间具有独立性,但是为什么他们地址是一样的呢?

得出以下结论:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 但地址值是一样的,说明,该地址绝对不是物理地址
  • 在Linux地址下,这种地址叫做 虚拟地址

二、🌟地址空间

2.1 💬如何理解地址空间

进程 = 进程控制块(task_struct)+ 代码和数据

        操作系统维护进程不止通过进程控制块(task_struct),每个进程其实还存在一个地址空间,它将进程的代码和数据分成不同的区管理起来,地址从低到高有正文区初始化数据区未初始化数据区堆区共享区栈区命令行参数与环境变量区,但真正的数据并不保存在地址空间中,地址空间仅提供连续线性的一串地址,这种地址叫虚拟地址, 通过虚拟地址映射到数据真正存储的物理地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址,物理地址用户一概看不到,由OS统一管理,操作在Linux操作系统中地址空间是用一个叫 mm_struct 的结构体维护起的

        虚拟地址映射到物理地址要通过页表,目前可以把他理解为保存着虚拟地址到物理地址映射关系的表

        

2.2 💬问题解释(写实拷贝)

        父进程创建子进程的时候,子进程会将父进程许多的内核数据结构都拷贝一份,其中就包括了页表,父进程在页表中保存了g_val的映射关系,所以子进程也可以访问到g_val,这就是父子进程中g_val的值与地址相同的原因。

        当子进程要修改g_val时,由于父子进程对g_val映射到相同的物理地址,父进程中的g_val也会随之修改,但是为了满足进程独立性的特点,操作系统会在物理内存中重新开辟一段新的空间,

并将g_val的值拷贝过来,这时子进程的页表就会重新映射物理地址,这时子进程修改g_val,父进程就不会收到影响了,这个操作全程由操作系统自主完成,称之为写实拷贝

ps:如果一个全局变量父子进程都不进行修改,两者是进行共享的,并不会直接让子进程拷贝一份,这时因为许多变量父子进程一般不会进行修改,但这些数据却很大,比如说环境变量等,直接拷贝一份会很浪费空间,所以当其中一方要修改时,才会进行拷贝,通过调整拷贝的时间顺序,达到节省空间的效果

2.3 💬地址空间的意义

1. 让无序变成有序,让进程以同意的视角看待物理内存,以及自己运行的各个区域

【解释】:

        一个进程的代码和数据在物理内存中的存储不一定是连续的,可能会根据数据类型的不同分别存储在物理内存的各个地方,此时进程要管理这些数据,就需要在task_struct中讲这些散乱的数据分别管理起来,当进程很多的时候,就会造成数据混乱,不利于操作系统的内存管理。

        而每个进程拥有自己独立的地址空间后,就不会被物理内存中的杂乱的数据影响了,管理数据只需要将虚拟地址通过页表映射到物理内存中即可

2. 进程模块与内存管理模块发生解耦

【解释】:

        当我们在写C语言程序时,比方说要使用堆空间,可以利用malloc函数开辟好,但是可能代码跑了很长时间后才会使用这段空间,那这段时间别的进程就无法利用这段空间了,会造成空间的浪费,有了虚拟地址和页表的概念,当进程需要开辟空间时,操作系统只需要将虚拟地址填入页表,但并不构建映射关系,当进程需要使用这段空间时,操作系统才会在物理内存开辟好空间,再构建映射关系,这样就可以大大提高空间的利用率。通过虚拟地址和页表构建映射关系等操作为进程的管理,在物理内存开辟空间等操作为内存管理,地址空间可以使两模块发生解耦,提高系统的资源利用

3. 拦截非法请求,保护操作系统

  【解释】:   

        当进程想访问一个地址的内容时,操作系统会先检查页表中是否存在该虚拟地址,如果不存在,就拦截这个请求,并报错提醒,防止其向物理内存修改数据,这就是为什么我们写程序有非法访问时程序会报错,而不是操作系统直接崩溃的原因

2.4 深入理解页表与写实拷贝

        其实页表并没有上述讲的这么简单,其内部还有许多寄存器和字段信息等,例如数据是否存在内存中、rwx权限等,我们可以举两个例子理解一下,本文只是初识地址空间,关于页表的更多信息后续会讲。

(1) 理解常量区修改

        我们在写程序时,对常量区的内容进行修改时程序会发生报错,那为什么修改别的区的内容是程序就不报错呢?这是由于页表中存在着对数据rwx权限判断的字段,操作系统会讲常量的数据权限修改为只读,当我们要修改常量区内容时,操作系统拿着虚拟地址在页表中找映射关系进行修改,发现该数据没有写权限,就会拦截这个请求,并报错,保护物理内存。

(2)系统是如何检测写实拷贝的

        当父进程创建子进程时,会将父子进程对数据的rwx权限仅保留读权限,当父子任意一方想要修改数据时,就会检测到错误,这时操作系统就会检测是否要发生写实拷贝

  (3) 如何理解fork函数返回两个返回值

        当fork( )函数运行完代码,会return一个返回值,pid_t id = fork() ,return的本质就是修改id

的值,此时就会发生写实拷贝,父子进程就会各自返回一个值

      

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

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

相关文章

单元测试框架 Junit

目录 什么是Junit&#xff1f; Junit的基础注解有哪些&#xff1f; 什么是参数化&#xff1f;参数化通过哪几种方式传输数据&#xff1f; 单参数 多参数 CSV文件获取参数 方法获取参数 测试用例执行顺序如何控制&#xff1f; 什么是断言assert&#xff1f;Assertions类…

18、【qlib】【其他组件/特性/主题】序列化

序列化 简介 Qlib支持将DataHandler、DataSet、Processor、Model等组件的状态保存至磁盘并重新加载。 可序列化类 Qlib提供了一个基础类qlib.utils.serial.Serializable,其状态可以以pickle格式保存到磁盘或从磁盘加载。当用户将一个Serializable实例的状态dump至磁盘时,…

JVM面试专题

文章目录 JVM 内存模型及分区1. 堆区&#xff08;Heap&#xff09;2. 栈区&#xff08;Stack&#xff09;3. 方法区&#xff08;Method Area&#xff09;4. 本地方法栈&#xff08;Native Method Stack&#xff09;5. 程序计数器&#xff08;Program Counter&#xff09; 堆内存…

AOF 持久化是怎么实现的?

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) AOF 日志 试想一下&#xff0c;如果 Redis 每执行一条写操作命令&#xff0c;就把该命令以追加的方式写入到一个文件里&#xff0c;然后重启 Redis 的时候&#xff0c;先去读取这个文件里的命令&#xf…

说道说道JSP和HTTP吧

大家都知道的是JSP&#xff08;Java Server Pages&#xff09;&#xff0c;也知道有个传输协议为HTTP协议&#xff0c;那么他俩到底有啥关系&#xff1f;像是有关系但又有点说不清楚&#xff0c;这里咱们一块捋一捋。 我们说servlet处理客户端请求的方式有2种&#xff1a;get和…

使用Intellij idea编写Spark应用程序(Scala+Maven)

使用Intellij idea编写Spark应用程序(ScalaMaven) 对Scala代码进行打包编译时&#xff0c;可以采用Maven&#xff0c;也可以采用sbt&#xff0c;相对而言&#xff0c;业界更多使用sbt。这里介绍IntelliJ IDEA和Maven的组合使用方法。IntelliJ IDEA和SBT的组合使用方法&#xf…

FANUC机器人零点标定的基本步骤(出厂数据)

FANUC机器人零点标定的基本步骤(出厂数据) FANUC 零点数据存在问题的机器人通常会出现以下几种报警: (1)SRVO-062报警 - 脉冲编码器数据丢失,机器人完全不能动,具体消除方法可参考以下链接中的内容: FANUC机器人SRVO-062报警原因分析及处理对策 (2)SRVO-075报警 -…

自己动手做一个批量doc转换为docx文件的小工具

前言 最近遇到了一个需求&#xff0c;就是要把大量的doc格式文件转换为docx文件&#xff0c;因此就动手做了一个批量转换的小工具。 背景 doc文件是什么&#xff1f; “doc” 文件是一种常见的文件格式&#xff0c;通常用于存储文本文档。它是 Microsoft Word 文档的文件扩…

探索 Flutter 中的 NavigationRail:使用详解

1. 介绍 在 Flutter 中&#xff0c;NavigationRail 是一个垂直的导航栏组件&#xff0c;用于在应用程序中提供导航功能。它通常用于更大屏幕空间的设备&#xff0c;如平板电脑和桌面应用程序。NavigationRail 提供了一种直观的方式来浏览应用程序的不同部分&#xff0c;并允许…

PyStructureFactor:隧道电离率中分子结构因子的 Python 代码

PyStructureFactor&#xff1a;隧道电离率中分子结构因子的 Python 代码 隧道电离是强场和阿秒科学的核心。在本文中&#xff0c;我们提出了PyStructureFactor——一个通用的Python代码&#xff0c;用于计算强激光场下常见分子的隧道电离率的结构因子。数值实现基于积分表示…

004——内存映射(基于鸿蒙和I.MAX6ULL)

目录 一、 ARM架构内存映射模型 1.1 页表项 1.2 一级页表映射过程 1.3 二级页表映射过程 1.4 cache 和 buffer 二、 鸿蒙内存映射代码学习 三、 为板子编写内存映射代码 3.1 内存地址范围 3.2 设备地址范围 一、 ARM架构内存映射模型 &#xff08;以前我以为页表机制…

使用插件将swagger文档转html或pdf

github上有maven开源插件swagger2markup将swagger文档转为.adoc格式的文档&#xff0c;另外一个maven开源插件asciidoctorj-pdf则可以将.adoc格式的文档转为html和pdf。由于GitHub访问不稳定&#xff0c;在gitee上有镜像项目。所以我就贴gitee上的项目地址了。 实现从swagger文…

Python下载音乐

今天我就来分享一下我的方法:Python爬虫 在CS dn社区中我浏览了许多关于爬虫代码&#xff0c;可都有各自的缺陷&#xff0c;有的需要ID比较麻烦&#xff0c;这里我编写了一个程序&#xff0c;他只需要输入歌曲名字即可进行搜索爬取并下载 话不多说&#xff0c;下面的程序复制…

局域网内的手机、平板、电脑的文件共享

在日常工作生活中&#xff0c;经常需要将文件在手机、平板、电脑间传输&#xff0c;以下介绍三种较为便捷的方法&#xff1a; 1.LocalSend 该软件是免费开源的&#xff0c;可以在局域网内的任意手机、平板、电脑间传递文件&#xff0c;并且任意一方都可以作为“发送方”和“接…

MapReduce学习问题记录

1、如何跳过对某行数据的处理 第一行数据是字段名不需要处理&#xff0c;我们知道第一行偏移量是0&#xff08;行记录的时候是从数组首地址开始&#xff0c;到了行标识符进行一次计数&#xff0c;这个计数就是行偏移量&#xff0c;从0开始&#xff09;&#xff0c;我们根据偏移…

线程池的7大参数

线程池的7大参数 一、 corePoolSize 线程池核心线程大小 核心线程永远不会销毁&#xff0c;即使他们处于空闲状态&#xff0c;除非设置了allowCoreThreadTimeOut。任务提交到线程池后&#xff0c;首先会检查当前线程数是否达到了corePoolSize&#xff0c;如果没有达到的话&…

【绘图案例-奇偶填充规则 Objective-C语言】

一、接下来,我们来学习奇偶填充规则 1.就是说,你在填充的时候,实际上,是有一些规则的, 奇偶填充规则:even-odd rule, even:偶数,odd:奇数 2.把之前的copy代码,复制粘题一份,名字改成“07-奇偶填充规则”, 把ppt里的代码复制粘贴过来, 我们来看一下这段儿代码,…

CSS(一)

一、CSS 简介 1.1 HTML 的局限性 说起 HTML&#xff0c;这其实是个非常单纯的家伙&#xff0c;他只关注内容的语义。比如 <h1> 表明这是一个大标题&#xff0c;<p> 表明这是一个段落&#xff0c;<img> 表明这儿有一个图片&#xff0c;<a> 表示此处有链…

每日一题 --- 设计链表[力扣][Go]

设计链表 题目&#xff1a;707. 设计链表 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则…

(九)任务通知

一、概念 &#xff08;1&#xff09;FreeRTOS从V8.2.0版本开始提供任务通知 &#xff08;2&#xff09;每个任务都有一个32位的通知值 &#xff08;3&#xff09;发送任务通知的几种情况 3.1 发送通知给任务&#xff0c;如果有通知未读&#xff0c;不覆盖通知值 3.2 发送通知给…