【Linux】进程概念IV 进程地址空间

img

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。

本篇导航

  • 0. 数据在内存中的分布
  • 1. 虚拟地址与真实物理地址
  • 2. 进程地址空间
    • 2.1 进程地址空间概念
    • 2.2 进程->页表->内存

在这里插入图片描述

0. 数据在内存中的分布

我们熟知的栈区堆区等在内存中的分布是怎样的呢?

地址空间中有着这几样区域:

  1. 代码区
  2. 字符常量区
  3. 已初始化全局变量
  4. 未初始化全局变量
  5. 堆区
  6. 栈区
  7. 命令行参数
  8. 内核空间

他们在内存中的分布是由上到下,满足下面这个模型.

228dfa789007f98aa0f45743545d024

代码区在低地址空间.地址从下往上增长. 堆区向上申请地址,栈区向下申请地址

我们可以来验证一下这个布局正确与否

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
int gval=100;
int initgval=100;
int uninitgval;
int main()
{printf("code address :%p\n",main);const char* str="hhhhh";printf("str address : %p\n",str);int a=0;int b=0;int c=0;static int sta=0;printf("initgval:%p\n",&initgval);printf("uninitgval:%p\n",&uninitgval);printf("static:%p\n",&sta);printf("stack address:%p\n",&a);printf("stack address:%p\n",&b);printf("stack address:%p\n",&c);int* heapadd1=(int*)malloc(100);int* heapadd2=(int*)malloc(100);printf("stack address:%p\n",&heapadd1);printf("stack address:%p\n",&heapadd2);printf("heap address:%p\n",heapadd1);printf("heap address:%p\n",heapadd2);return 0;
}

我们使用main函数地址来表示代码区,const char* str代表字符常量区.最后发现

image-20231110192754517

大部分和我们之前所说一致.

我们可以发现static修饰的变量为什么能在函数结束时被保存下来呢?因为其存放在了全局变量去,

但栈似乎并不是往下增长的.这是由于操作系统的操作导致的,不同的操作系统可能测试出来的结果并不相同.但内存分布是相同的.

1. 虚拟地址与真实物理地址

我们使用fork函数创建一个子进程,在子进程中改变与父进程同名的变量.父进程中该变量会被改变嘛?

#include<stdlib.h>                                                                                         
#include<stdio.h>
#include<unistd.h>
int main()
{int val=100;pid_t id = fork();if(id==0){val=99;printf("i am a childre process val is: %d address is %p\n",val,&val);sleep(1);}elseprintf("i am a father process val is: %d address is %p\n",val,&val);return 0;
}

编译执行会出现下面的结果

image-20231110194550166

我们可以发现 子进程将val变量值更改了,而父进程却不受影响.但他们两个指向的是同一个地址空间.

相同的地址空间怎会存储两个不一样的值呢? 他们使用的都是虚拟地址空间,而虚拟空间与真实地址空间中存在映射关系.

2. 进程地址空间

2d5d4599f712724b866e3caf71e45ec

每个进程都会拥有一个这样的内存分布+页表.

上面的情况是:子进程复制了父进程的页表与内存分布.其中子进程的页表与父进程的页表虚拟地址真实地址都相同.当向一个共享的变量写入数据时,会发生写时拷贝.在内存中重新给子进程申请一片空间,在这里进行写入.但是虚拟地址不变,只是改变了虚拟地址与物理地址的映射关系.

重新开辟空间这一操作,对于左边的虚拟地址,或者说对于进程来说是不可知的.这满足了软件中的解耦合设计.左边的进程管理只需要管理好进程,不需要去操作内存.

2.1 进程地址空间概念

在PCB中也即在Task_Struct中会存在一段mem结构体指针.mem结构体中存了每个区域(代码、堆区…)的起始与终止位置

对线性空间进行区域划分.

我们来看看Linux内核2.6.1中关于这段的具体描述

struct mm_struct{

image-20231110202059772

}

每一个段区域中每个最小单位都有自己的地址可以 供进程直接使用

这样做可以让进程以相同的方式看待内存,不必去管内存中哪里为空,当下需要将数据放到哪里.只需要管好自己的内存分布即可.

我们之前如果想要去修改一个常量区的字符,或者指针越界访问,操作系统会抛出警告.那么这是如何做到的呢?

通过页表.页表中有一个标志位,可以标志当前存入的数据的属性.当我们通过页表去访问内存时,操作系统会去检查本次操作是否合法.进而保护物理内存

每个进程是完全独立的,每个进程都认为自己拥有了所有的内存.进程需要使用内存时通过操作系统向内存模块进行申请.这样的设计让内存模块与进程模块实现了解耦合

**进程PCB中具有几个属性描述了当前内存分布以及页表所在的空间.当进程被放在CPU上时,会将这些信息提供给CPU.反之,从内存中拿下时,也会将这些信息带走.**所以每个进程间都是互相独立的.

2.2 进程->页表->内存

我们平常玩游戏,一个游戏的大小大多时候是远大于我们的内存的.显然不可能一次性将全部的内容加载到内存当中.那么我们怎么保证当前进程所访问的内容在使用时一定存在呢?

  • 操作系统对大文件以一种惰性加载的方式实现分批加载

进程通常假设自己要使用的资源已经全部加入到内存当中.(解耦合暗示了是否加载到真实的物理地址中进程并不关心),之后为这些资源分配虚拟的地址空间.

当进程访问这些虚拟的地址空间时,操作系统会检查虚拟空间对应的物理内存是否存在?
若不存在则发生却缺页中断,该进程被挂起.此时CPU向内存管理模块去申请对应的资源.待准备完毕再反馈给进程

所以现代操作系统不做任何浪费空间和时间的事情
image-20230905164632777

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

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

相关文章

excel表的筛选后自动求和

一般都使用subtotal函数。 通过看一个大佬的视频&#xff0c;发现可以有更简单的方法。 首先任意筛选数据(ctrlshiftl)&#xff0c; 然后选中需要求和的列的最下方的空白单元格&#xff0c;再按alt。 回车即可。 实质它还是用的subtotal函数

(11.13 知识总结(路由层)

一、路由层 1.1路由匹配 1.1.1 什么是路由&#xff1f; 路由可以看成是跟在 ip 和 port 之后的地址 1.1.2 url( ) 方法 # 示例 urlpatterns [ url(r^admin/, admin.site.urls), url(r^login/, views.login_func), url(r^register/$, views.register_func), ] url…

使用swagger-typescript-api

引言 前后端分离大致是这样的 后端&#xff1a;控制层 / 业务层 / 数据操作层前端&#xff1a;控制层 / 视图层 前后端的控制层&#xff0c;实际上就是前后端接口的对接 前后端分离&#xff0c;实现了更好地解耦合&#xff0c;但也引入了接口对接的过程&#xff0c;这个过程…

Clickhouse学习笔记(10)—— 查询优化

单表查询 Prewhere 替代 where prewhere与where相比&#xff0c;在过滤数据的时候会首先读取指定的列数据&#xff0c;来判断数据过滤&#xff0c;等待数据过滤之后再读取 select 声明的列字段来补全其余属性 简单来说就是先过滤再查询&#xff0c;而where过滤是先查询出对应…

设置专属链接的这些作用你知道吗?

专属链接作为一种个性化的链接&#xff0c;用于为特定的客户或群体提供定制化的体验或服务。对于企业来说&#xff0c;每个渠道或者每个客户都能拥有一个专属链接是无比便利的事情。企业可以将这个链接嵌入到各种宣传物料中&#xff0c;让客户通过输入链接即可进入与客服的交流…

挑战100天 AI In LeetCode Day08(热题+面试经典150题)

挑战100天 AI In LeetCode Day08&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-102.1 题目2.2 题解 三、面试经典 150 题-103.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

upload-labs关卡6(基于黑名单的大小写绕过)通关思路

文章目录 前言一、回顾靶场第五关二、靶场第六关需要了解的知识点1、window系统大小写不敏感2、linux系统大小写敏感 三、靶场第六关通关思路1.看源代码2.通过bp进行大小写绕过 总结 前言 此文章只用于学习和反思巩固文件上传漏洞知识&#xff0c;禁止用于做非法攻击。注意靶场…

【SpringBoot】SpringBoot自动配置底层源码解析

概述 EnableAutoConfiguration源码解析SpringBoot常用条件注解源码解析SpringBoot之Mybatis自动配置源码解析SpringBoot之AOP自动配置源码解析SpringBoot Jar包启动过程源码解析 DeferredImportSelector接口 DeferredImportSelector和ImportSelector的区别在于&#xff1a; …

海康Visionmaster-通讯管理:使用 Modbus TCP 通讯 协议与流程交互

使用 Modbus TCP 通讯协议与视觉通讯&#xff0c;当地址为 0000 的保持型寄存器(4x 寄存器)变为 1 时&#xff0c;触发视觉流程执行一次&#xff0c;同时视觉将地址为 0000 的寄存器复位&#xff08;也即写为 0&#xff09;&#xff0c;视觉流程执行完成后&#xff0c;将结果数…

更安全的ssh协议与Gui图形化界面使用

目录 前言&#xff1a; 一.Gui图形化界面的使用 二.ssh协议 SSH的主要作用包括&#xff1a; 相比其他网络协议&#xff0c;SSH的优势包括&#xff1a; 三.idea集成Git 前言&#xff1a; 上一篇讲解了git的命令用法以及https协议&#xff0c;但是这个协议放在做团队项目的…

win7纯净版没有网卡驱动怎么办(msdn重装的系统)

当电脑重新安装Windows7系统之后&#xff0c;发现无法连接网络的情况&#xff0c;可以找一台能正常上网使用的电脑&#xff0c;打开浏览器软件&#xff0c;搜索“360驱动大师”&#xff0c;下载360驱动大师网卡版到U盘&#xff0c;然后拷贝到自己的win7电脑上安装网卡驱动&…

Leetcode—67.二进制求和【简单】

2023每日刷题&#xff08;二十八&#xff09; Leetcode—67.二进制求和 实现代码 void reverse(char *a, int len) {for(int i 0; i < len / 2; i) {char tmp a[i];a[i] a[len - 1 - i];a[len - 1 - i] tmp;} }char* addBinary(char* a, char* b) {int len1 strlen(a…