linux进程(进程地址空间)

news2024/10/7 14:25:28

目录

前言:

正文:

1.验证地址空间 

2.地址空间是指物理空间吗

3.linux内核的地址空间

4进程访问地址

4.1早期程序寻址 

4.2进程地址空间到物理内存的映射

4.3解释同一变量产生不同值

5虚拟地址空间的意义 

5.1保护物理内存 

5.2进程管理和内存管理的解耦

5.3方便管理 

6总结 


前言:

对于 C/C++ 来说,程序中的内存包括这几部分:栈区堆区静态区 等,其中各个部分功能都不相同,比如函数的栈帧位于 栈区,动态申请的空间位于 堆区,全局变量和常量位于 静态区 ,区域划分的意义是为了更好的使用和管理空间,那么 真实物理空间 也是如此划分吗?多进程运行 时,又是如何区分空间的呢?写时拷贝 机制原理是什么?本文将对这些问题进行解答

 

正文:

1.验证地址空间 

 我们通过代码验证一下,各部分数据处于的位置

  

#include <stdio.h>    
#include <unistd.h>    
#include <stdlib.h>    
    
int g_unval;    
int g_val = 100;    
    
    
int main(int argc, char *argv[], char *env[])    
{    
   // int a = 10;    
    //字面常量    
   const char *str = "helloworld";    
   // 10;    
   // 'a';    
    printf("code addr: %p\n", main);    
    printf("init global addr: %p\n", &g_val);    
    printf("uninit global addr: %p\n", &g_unval);    
    
    char *heap_mem = (char*)malloc(10);    
    char *heap_mem1 = (char*)malloc(10);    
    printf("heap addr: %p\n", heap_mem); //heap_mem(0), &heap_mem(1)    
    printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)    
                                                                                                                                                             
    printf("stack addr: %p\n", &heap_mem); //heap_mem(0), &heap_mem(1)    
    printf("stack addr: %p\n", &heap_mem1); //heap_mem(0), &heap_mem(1)    
    
    printf("read only string addr: %p\n", str);    
    int i;    
    for(i = 0 ;i < argc; i++)
    {
        printf("argv[%d]: %p\n", i, argv[i]);
    }
    for(i = 0; env[i]; i++)
    {
        printf("env[%d]: %p\n", i, env[i]);
    }
 
    return 0;
}

2.地址空间是指物理空间吗

我们利用fork()系统调用函数创建子进程,让父子进程共同使用同一个变量

 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main()
{
  int val = 10;
  pid_t id = fork();
  if(id == 0)
  {
  	val *= 2;	//刻意改变共享值
    printf("我是子进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n", getpid(), getppid(), val, &val);
    exit(0);
  }

  waitpid(id, 0, 0);

  printf("我是父进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n", getpid(), getppid(), val, &val);
  return 0;
}

 

怎么同一个变量,同一个地址,同时读取读到了不同内容呢!!如果地址空间是实实在在的物理地址,同一地址是不可嗯呢读取到两个值,所以结论就是地址空间是虚拟地址也叫线性地址,语言层面包括c/c++都是虚拟地址,用户无法看到真实地址,由操作系统统一管理。

3.linux内核的地址空间

地址空间本质就是一种内核数据结构,在Linux当中,叫做struct mm_struct(linux内核当中的地址空间结构体)包含了一些区域信息(先描述),能够实现区域划分(本质就是在一定的范围内定义start和end)。 

struct mm_struct
{
    unsigned long code_start;
    unsigned long code_end;
    
    unsigned long init_start;
    unsigned long init_end;
    
    unsigned long uninit_start;
    unsigned long uninit_end;
    
    unsigned long heap_start;
    unsigned long heap_end;
    
    unsigned long stack_start;
    unsigned long stack_end;
    //...等不同的区域划分
}

 每个进程都会有自己的地址空间,同时进程控制块(PCB)中也包含了 *mm_struct 指针,可使我们直接找到自己所对应的进程地址空间(后组织)。

  上述讲述的这么多,我们可以理解为进程地址空间就是操作系统给进程花了一个大饼。

  这个大饼就是指的每个进程都会有4GB的连续的空间(0x00000000~0xFFFFFFFF)。实际上呢,这4GB的的空间是虚拟内存,虚拟内存对应的实际物理内存,可能只对应的分配了一点点的物理内存,实际使用了多少内存,就会对应多少物理内存。

  这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它的数据是存储在多个物理内存碎片的,还有一部分存储在外部磁盘存储器上,在需要时将数据交换进物理内存。

4进程访问地址

4.1早期程序寻址 

在虚拟地址出现之前,程序的寻址都是直接寻找的物理地址。但是这样会有很多的不足:

直接访问物理内存不安全。例如我们假如使用了野指针,对内存中的数据进行了修改,那么这个时就会影响到其他的进程;
因为物理内存是有限的,当有多个进程要执行的时候,对每个进程都要分配4G内存,很显然你内存若小一点,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的。
因为内存是随机分配的,所以程序运行的地址也是不正确的。例如下面的代码就会出现野指针的问题

4.2进程地址空间到物理内存的映射

为了解决各种问题,大佬们提出了 虚拟地址空间 这个概念,有了 虚拟空间 后,当进程创建时,系统会为其分配属于自己的 虚拟空间需要使用内存时,通过 寻址 的方式,使用物理地址上的空间即可。如下图所示:

 当有多个进程,具体情况就如下图所示:

        地址空间和页表是每个进程都独有的一份,只要保证每一个进程的页表,能够映射到不同区域的物理内存,就能够做到进程之间互不干扰。这就是我们所说的进程所具有独立性。

          映射是由谁来完成的呢?答案是操作系统!操作系统通过地址转换机制将虚拟地址映射到物理地址,以实现对内存的访问。这种映射通常在页表或段表等数据结构上实现,其中存储了虚拟地址与物理地址之间的映射关系。 

4.3解释同一变量产生不同值

 相同的地址打印出不同的数据。注意,我们所访问到的地址都是虚拟地址。并不是物理地址。我们创建了一个子进程,子进程本身是继承了父进程的数据和代码。在没有对数据进行修改之前,子进程和父进程共享了一份数据。一但对子进程或者父进程的数据进行修改,就会发生写时拷贝。对修改的数据进行深拷贝,从而达到对彼此不产生干扰,实现进程独立性。

  那就对相同地址打印出不同数据的现象不难理解了。当我们对父进程的数据进行修改时,父进程发生了写时拷贝,在内存中开辟了空间。但他们都有自己的地址空间(虚拟地址),所以地址相同也是正常现象(子进程继承父进程的代码和数据)。即使虚拟地址一样,但是可通过页表映射到不同的物理内存中。具体如下图:

5虚拟地址空间的意义 

5.1保护物理内存 

 

可能还有一些疑惑:即使有了虚拟地址,那我要是对野指针进行了访问修改,页表对野指针映射后,还不是对物理内存进行了非法的访问修改吗?地址空间的设置不就多此一举了吗?

  事实并非上述一样。凡是非法的访问或者映射,操作系统都会识别到的。一但你进行了非法的访问或者映射,操作系统就会终止掉你的程序。举个例子,当我们对野指针进行访问修改时,你的程序就会崩溃,这不就是程序终止退出吗!!!

  地址空间有效的保护了物理内存。因为地址空间和页表是操作系统创建并且维护的。这也就意味着地址空间和页表进行映射时需要操作系统进行监管!

5.2进程管理和内存管理的解耦

 

因为有了地址空间和页表,所以我们的数据可以在物理内存中的任何合法位置加载。因为他们之间有映射。物理内存的分配和进程的管理可以做到没有关系!

  所以进程模块和内存模块只需要各自完成各自的事情,最后通过页表的映射将他们连接起来,产生关系。降低了他们之间互相的影响度。

5.3方便管理 

都可以从ox0000开始,同喜视角看待内存结构,让无序的物理地址有序化方便管理。

6总结 

 

我们平常所访问到的地址均为虚拟地址。地址空间并不是物理地址,而是虚拟地址。通过页表映射访问物理地址。 每个进程都有自己的地址空间和页表。

  页表是一种数据结构,它存储了虚拟地址与物理地址之间的映射关系。在进行地址转换时,操作系统根据进程的页表查找对应的物理地址,然后将虚拟地址转换为物理地址,以便进行实际的内存访问。

  通过使用虚拟地址,操作系统可以为每个进程提供独立的地址空间,使得多个进程可以并发运行,彼此之间相互隔离,互不干扰。虚拟地址还提供了更高的灵活性和保护性,使得操作系统可以有效地管理和分配内存资源,提高系统的性能和安全性。

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

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

相关文章

DolphinScheduler-3.2.0 集群搭建

本篇文章主要记录DolphinScheduler-3.2.0 集群部署流程。 注&#xff1a;参考文档&#xff1a; DolphinScheduler-3.2.0生产集群高可用搭建_dophinscheduler3.2.0 使用说明-CSDN博客文章浏览阅读1.1k次&#xff0c;点赞25次&#xff0c;收藏23次。DolphinScheduler-3.2.0生产…

VitePress-13- 配置-title的作用详解

作用描述 1、title 是当前站点的标题&#xff1b;2、默认值是 &#xff1a;VitePress&#xff1b;3、当使用默认主题时&#xff0c;会直接展示在 页面的【导航条】中&#xff1b;4、一个特殊的作用 &#xff1a; 会作为单个页面的默认标题后缀&#xff01;除非又指定了【title…

[韩顺平]python笔记

AI工程师、运维工程师 python排名逐年上升&#xff0c;为什么&#xff1f; python对大数据分析、人工智能中关键的机器学习、深度学习都提供有力的支持Python支持最庞大的 代码库 &#xff0c;功能超强 数据分析&#xff1a;numpy/pandas/os 机器学习&#xff1a;tensorflow/…

【算法与数据结构】42、LeetCode接雨水

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;   程序如下&#xff1a; 复杂度分析&#xff1a; 时间复杂度&#xff1a; O ( ) O() O()。空间复…

hook函数——useRef

useRef useRef 是一个 React Hook&#xff0c;它能帮助引用一个不需要渲染的值。也就是说useRef可以存储一个值&#xff0c;但是不被组件渲染&#xff0c;仅仅只是引用&#xff0c;主要包括两个方面&#xff0c;例如使用ref引用一个值&#xff0c;使用ref引用一个dom节点&…

「C++ 类和对象篇 10」初始化列表

目录 一、什么是初始化列表&#xff1f; 二、为什么需要初始化列表&#xff1f; 三、初始化列表怎么使用&#xff1f; 3.1 在构造函数中使用初始化列表 3.2 注意 3.3 结论 3.4 应用场景 四、初始化列表的初始化顺序 五、另一种初始化成员变量的方法 【总结】 一、什么是初始化…

openresty (nginx)快速开始

文章目录 一、什么是openresty&#xff1f;二、openresty编译安装1. 编译安装命令1.1 编译完成后路径1.2 常用编译选项解释 2. nginx配置文件配置2.1 nginx.conf模板 3. nginx常见配置一个站点配置多个域名nginx配置中location匹配规则 三、OpenResty工作原理OpenResty工作原理…

[经验] 喉咙沙哑的原因及应对方法是什么 #学习方法#其他#媒体

喉咙沙哑的原因及应对方法是什么 生活中&#xff0c;喉咙不舒服是很常见的情况&#xff0c;尤其是喉咙沙哑&#xff0c;让人感到特别难受&#xff0c;影响睡眠和生活质量。那么喉咙沙哑怎么办呢&#xff1f;接下来我会分享一些简单易行的方法&#xff0c;帮助你缓解这种不适感…

kali最新最简单安装

之前都是用iso镜像文件的 今年好多东西都删库了&#xff0c;所有还是要主要资源的保存 去官网找下载 一般来说都是用虚拟机的 下载完会是一个压缩文件&#xff0c; 解压&#xff0c;然后操作之前需要先下载虚拟机 打开方式用虚拟机打开 kali就按装好了

最简单的基于 FFmpeg 的音频编码器(PCM 编码为 AAC)

最简单的基于 FFmpeg 的音频编码器&#xff08;PCM 编码为 AAC&#xff09; 最简单的基于 FFmpeg 的音频编码器&#xff08;PCM 编码为 AAC&#xff09;正文结果工程文件下载其他参考链接 最简单的基于 FFmpeg 的音频编码器&#xff08;PCM 编码为 AAC&#xff09; 参考雷霄骅…

网络原理(一)

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;网络原理(一) 一. 应用层 应用层是和程序员联系最密切的一层,对于应用层来说,程序员可以自定义应用层协议,应用层的协议一般要约定好以下两部分内容: 根据需求,明确要传输哪些信…

使用REQUESTDISPATCHER对象调用错误页面

使用REQUESTDISPATCHER对象调用错误页面 问题陈述 InfoSuper公司已经创建了一个动态网站。发生错误时,浏览器中显示的堆栈跟踪很难理解。公司的系统分析师David Wong让公司的软件程序员Don Allen创建自定义错误页面。servlet引发异常时,应使用RequestDisapatcher对象向自定义…

[VulnHub靶机渗透] Misdirection: 1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

jmeter-问题二:JMeter进行文件上传时,常用的几种MIME类型

以下是一些常用的MIME类型及其对应的文件扩展名&#xff1a; 文本类型: text/plain: 通常用于纯文本文件&#xff0c;如 .txt 文件。 text/html: 用于HTML文档&#xff0c;即 .html 文件。 application/msword: Microsoft Word文档&#xff0c;即 .doc 和 .docx 文件。 图像…

在Ubuntu22.04上部署ComfyUI

ComfyUI 是 一个基于节点流程的 Stable Diffusion 操作界面&#xff0c;可以通过流程&#xff0c;实现了更加精准的工作流定制和完善的可复现性。每一个模块都有特定的的功能&#xff0c;我们可以通过调整模块连接达到不同的出图效果&#xff0c;特点如下&#xff1a; 1.对显存…

2024年 前端JavaScript入门到精通 第一天

主要讲解JavaScript核心知识&#xff0c;包含最新ES6语法&#xff0c;从基础到API再到高级。让你一边学习一边练习&#xff0c;重点知识及时实践&#xff0c;同时每天安排大量作业&#xff0c;加深记忆&#xff0c;巩固学习成果。 1.1 基本软件与准备工作 1.2 JavaScript 案例 …

C语言rand随机数知识解析和猜数字小游戏

rand随机数 rand C语言中提供了一个可以随机生成一个随机数的函数&#xff1a;rand&#xff08;&#xff09; 函数原型&#xff1a; int rand(void);rand函数返回的值的区间是&#xff1a;0~RAND_MAX(32767)之间。大部分编译器都是32767。 #include<stdlib.h> int ma…

hummingbird,一个便于将模型部署到边缘设备的Python库!

前言 随着人工智能和机器学习的快速发展&#xff0c;将训练好的模型部署到生产环境中成为了一个重要的任务。而边缘计算设备&#xff0c;如智能手机、嵌入式系统和物联网设备&#xff0c;也需要能够运行机器学习模型以进行实时推理。Python Hummingbird 是一个强大的工具&…

Java学习笔记2024/2/8

面向对象 //面向对象介绍 //面向: 拿、找 //对象: 能干活的东西 //面向对象编程: 拿东西过来做对应的事情 //01-如何设计对象并使用 //1.类和对象 //2.类的几个不错注意事项 1. 类和对象 1.1 类和对象的理解 客观存在的事物皆为对象 &#xff0c;所以我们也常常说万物皆对…

基于springboot会员制医疗预约服务管理信息系统源码和论文

会员制医疗预约服务管理信息系统是针对会员制医疗预约服务管理方面必不可少的一个部分。在会员制医疗预约服务管理的整个过程中&#xff0c;会员制医疗预约服务管理系统担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类的管理系统也在不断改进。本课题所设计…