【Linux详解】进程地址空间

目录

研究背景

验证地址空间

实验一:父子进程变量地址一致性

实验二:变量值修改后父子进程的差异

分析与结论

实验三:进程地址空间验证

理解进程地址空间

区域与页表

写时拷贝机制

进程地址空间的意义

文章手稿:


xmind: 


研究背景

本文研究基于 Linux kernel 2.6.32 的32位平台进程地址空间的区别与实现。通过具体的代码示例和实验,揭示虚拟地址空间的概念,并探讨其重要性和操作系统对其管理的机制。

程序地址空间的回顾

在学习 C 语言时,常见的程序地址空间布局如下图所示:

#include <stdio.h>
#include <stdlib.h>
int main()
{printf("%s\n", getenv("PATH"));return 0;
}

上述代码展示了典型的程序地址空间结构,但我们对其理解并不深入。通过进一步的代码实验,可以更好地理解程序地址空间的概念。


验证地址空间

实验一:父子进程变量地址一致性

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //childprintf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parentprintf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}

输出结果(可能因环境而异):

实验二:变量值修改后父子进程的差异

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //childg_val=100;printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parentsleep(3);printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}

输出结果(可能因环境而异):

分析与结论

上述实验表明,父子进程的变量地址相同但内容不同,说明地址为虚拟地址,且父子进程有各自独立的物理地址映射。这验证了虚拟地址的概念,即我们在C/C++中看到的地址是虚拟地址,由操作系统负责将其转化为物理地址。

进程地址空间

程序地址空间实际上是进程地址空间的子集,是系统级的概念。进程地址空间通过虚拟地址映射实现内存独立性,确保进程间互不干扰。

实验三:进程地址空间验证

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int un_g_val;
int g_val = 100;int main(int argc, char* argv[], char* env[])
{printf("code addr            : %p\n", main);printf("init global addr     : %p\n", &g_val);printf("uninit global addr   : %p\n", &un_g_val);char* m1 = (char*)malloc(100);printf("heap addr            : %p\n", m1);printf("stack addr           : %p\n", &m1);int i = 0;for (i = 0; i < argc; i++) {printf("argv addr        : %p\n", argv[i]);   }for (i = 0; env[i]; i++) {printf("env addr         : %p\n", env[i]);}
}

运行结果

地址整体依次增大,堆区向地址增大方向增长,栈区向地址减少方向增长,验证了堆和栈的挤压式增长方向。

验证静态局部变量

静态修饰的局部变量,编译的时候已经被编译到全局数据区,这一点可以通过以下代码验证:

#include <stdio.h>
#include <stdlib.h>
void func() {static int static_var = 10;printf("static_var addr: %p\n", &static_var);
}
int main() {func();return 0;
}

结论


这也说明了这些变量的地址在全局数据区,而不是局部栈区。


理解进程地址空间

区域与页表

进程地址空间通过 mm_struct 结构体来管理各个区域。每个区域的定义如下:

struct mm_struct {long code_start;long code_end;long init_start;long init_end;long uninit_start;long uninit_end;long heap_start;long heap_end;long stack_start;long stack_end;...
}

用一个start 和end 就可以表示区域

每个区域都有一个 start 和 end,它们之间就有了地址,地址我们称之为虚拟地址,

 然后这些虚拟地址经过页表,就能映射到内存中了。

父子进程全局变量共享与写时拷贝

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int g_val = 100;
int main(void) 
{pid_t id = fork();if (id == 0) {// childint flag = 0;while (1) {printf("child: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);sleep(1);flag++;if (flag == 5) {g_val = 200;printf("child modified g_val\n");}}}else {// fatherwhile (1) {printf("parent: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);sleep(2);}}
}

运行结果

在父子进程中,虚拟地址相同但值不同,验证了写时拷贝机制。

写时拷贝机制

写时拷贝是指当父子进程有一方尝试修改变量时,操作系统会为修改方分配新的物理内存并拷贝数据,以确保独立性。

回顾:fork的两个返回值

pid_t id 是属于父进程的栈空间中定义的。

fork 内部 return 会被执行两次,return 的本质就是通过寄存器将返回值写入到接收返回值的变量中。当我们的 id = fork() 时,谁先返回,谁就要发生 写时拷贝。所以,同一个变量会有不同的返回值,本质是因为大家的虚拟地址是一样的,但大家的物理地址是不一样的。


进程地址空间的意义

虚拟地址空间通过软硬结合层,保护内存并简化进程和程序的设计和实现,确保进程的独立性和安全性。

表格:进程地址空间区域划分

区域类型起始地址结束地址
代码区code_startcode_end
初始化全局变量init_startinit_end
未初始化全局变量uninit_startuninit_end
堆区heap_startheap_end
栈区stack_startstack_end

那么有什么意义呢

拓展:os 对大文件的分批加载是怎么实现的呢

采用惰性加载的方式

存在 缺页中断 ,重新申请 填写页表

缺页中断:

当一个进程访问虚拟内存中的某一页时,操作系统会先检查该页是否当前已经被加载到物理内存中。如果这一页已经在物理内存中,CPU就可以直接访问它。但是,如果这一页并没有在物理内存中,就会发生缺页中断

当发生缺页中断时,CPU会暂停当前的执行,并将控制权交给操作系统内核。操作系统内核会首先查找页表,寻找到相关的页面对应的磁盘地址。然后,操作系统会将磁盘上的内容读取到空闲的物理内存页中。

一旦内容被加载到物理内存中,操作系统会更新页表,将该页面的映射关系添加到页表中,然后将控制权交还给进程并重新开始执行。这样,进程可以继续访问所需的内存页面。

整个过程用于解决虚拟内存中的页面不在物理内存中的问题,使得系统看起来好像比它实际拥有的更多内存一样,从而使得多个进程能够共享有限的内存资源,提高内存利用率和系统的整体性能。

就达到分批加载的效果啦

所以 进程 应该是先创建内核数据结构,再执行可执行程序的 

文章手稿:

 

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

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

相关文章

基于语音识别的智能电子病历(二)苹果端的语音接入

是2011年参与的&#xff0c;俺负责Wav文件处理、FTP通讯和一些后端部分。iPhone/iPad/iPod Recorder 前2年还在APP Store上 说明 Step-by-Step Procedure to Install App and Use the FnetRecorder Download the App from Apple Store Launch Apple Store and key in “fnetr…

深入理解LibTorch:从安装到API详解

摘要&#xff1a; 本文将详细介绍LibTorch的安装步骤和主要API的使用方法&#xff0c;帮助开发者快速上手并利用LibTorch进行深度学习应用开发。 正文&#xff1a; 一、LibTorch开发背景与优势 PyTorch是一个灵活且功能强大的深度学习框架&#xff0c;它的动态计算图特性使…

golang中空值判断函数,支持任意类型的空值判断

使用反射方式对any任意类型的数据是否为空判断, 可判断时间对象是否为空, 可判断所有数字类型,指针类型和结构体字符串是否为空 判断规则: bool类型因为只有true和false 所以 全部视为非空 nil 类型全部视为空 所有数字类型的 零值全部视为空 对应指针类型数据,只要是非ni…

labview排错

源代码正常跑&#xff0c;应用程序报这个错&#xff0c;是因为源代码的可以找到项目路径内所有dll的路径&#xff0c;而应用程序只能找到data文件夹的dll文件 解决查看源代码中.net的程序集的路径&#xff0c;复制对应的dll到data文件夹下 在执行developinterface.dll出现labv…

学习java第一百一十三天

Spring框架的好处&#xff1f; 轻量级&#xff1a;Spring框架是轻量级的&#xff0c;最基础的版本大约只有2MB。 控制反转&#xff08;IOC&#xff09;&#xff1a;通过控制反转技术&#xff0c;实现了解耦合。对象给出它们的依赖&#xff0c;而不是创建或查找依赖的对象。 …

雅思词汇及发音积累 2024.6.28

住宿 Student Accommodation Centre 学生住宿管中心 Student Accommodation Officer 学生住宿员 roommate/flatmate/dormmate/housemate 室友 housing coordinator /kəʊˈɔːdɪneɪtə(r)/ 住宿协调员 newspaper advertisement 报纸广告 landlord /ˈlndlɔːd/ 男房东 re…

深入探索Memcached:高效数据检索指南

标题&#xff1a;深入探索Memcached&#xff1a;高效数据检索指南 摘要 Memcached是一个高性能的分布式内存缓存系统&#xff0c;广泛用于提升Web应用程序的性能。它通过缓存数据和减少数据库查询来加速数据检索。本文将详细介绍如何从Memcached中检索数据&#xff0c;包括基…

R1快开门式压力容器操作历年真题含答案

单选题 1.安全联锁装置是用于( )目的的自动化装置&#xff0c;它通过机械或电气的机构使两个动作具有互相制约的关系。 A、自动化 B、安全&#xff08;正确答案&#xff09; 2.卡箍式快开门的安全连锁实现较为( )。 A、容易 B、困难&#xff08;正确答案&#xff09; 3.…

Python+Pytest+Allure+Yaml+Jenkins+GitLab接口自动化测试框架详解

PythonPytestAllureYaml接口自动化测试框架详解 编撰人&#xff1a;CesareCheung 更新时间&#xff1a;2024.06.20 一、技术栈 PythonPytestAllureYamlJenkinsGitLab 版本要求&#xff1a;Python3.7.0,Pytest7.4.4,Allure2.18.1,PyYaml6.0 二、环境配置 安装python3.7&…

Python operator模块这么用,效率杠杠的!

目录 1、基础操作符应用 🐍 1.1 加载operator模块 1.2 使用itemgetter进行排序 1.3 attrgetter与方法调用 2、高级功能探索 🔍 2.1 methodcaller的妙用 2.2 操作符重载与定制 3、结合lambda表达式 ✨ 3.1 lambda与operator模块协同工作 3.2 实战案例分析 4、结合…

【Spring Boot 事务管理】

Spring Boot 事务管理 一、Spring Boot中的事务管理1.声明式事务管理Transactional注解基本使用配置选项 2.编程式事务管理TransactionTemplatePlatformTransactionManager 二、Transactional注解深入1.基本使用基本属性 2.传播行为3.隔离级别4.事务超时设置5.回滚规则 三、事务…

如何保护磁盘数据?电脑磁盘数据怎么保护?

电脑磁盘是存储数据的基础&#xff0c;可以将各种重要数据保存在其中。为了避免数据泄露&#xff0c;我们需要保护磁盘数据。那么&#xff0c;电脑磁盘数据怎么保护呢&#xff1f;下面我们就一起来了解一下吧。 文件夹加密超级大师 文件夹加密超级大师是一款优秀的电脑数据加密…

人工智能 (AI) 基本概念 入门篇【C#】版

1. 什么是人工智能&#xff1f; 人工智能&#xff08;Artificial Intelligence, AI&#xff09;是指计算机系统能够执行通常需要人类智能的任务&#xff0c;如视觉识别、语音识别、决策和语言翻译等。AI的核心是通过算法和数据进行学习和推理&#xff0c;以实现智能行为。 2.…

说说MQ在你项目中的应用(二)商品支付

看了不少关于MQ的文章&#xff0c;也对MQ的作用做了一些总结。通常来说MQ有三大功能&#xff1a;异步处理、系统解耦和流量削峰。但我觉得这些功能本质上都是围绕着异步这个核心来的&#xff0c;只是针对不同的业务场景做了些调整。 现在市面上常用的MQ中间件&#xff0c;如Ra…

每天一个数据分析题(三百九十八)- 逻辑回归

逻辑回归的输出概率在[0,1]的范围内&#xff0c;逻辑回归使用以下哪个函数来实现概率转换&#xff1f; A. Sigmoid B. 求模 C. 平方 D. 几率单位 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Pyth…

力扣爆刷第154天之TOP100五连刷36-40(最长公共子序列、二分、二叉树右视图)

力扣爆刷第154天之TOP100五连刷36-40&#xff08;最长公共子序列、二分、二叉树右视图&#xff09; 文章目录 力扣爆刷第154天之TOP100五连刷36-40&#xff08;最长公共子序列、二分、二叉树右视图&#xff09;一、1143. 最长公共子序列二、94. 二叉树的中序遍历三、82. 删除排…

如何查看端口是否开放

如何查看端口是否开放 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在网络通信中&#xff0c;端口的开放状态对于应用程序的正常运行至关重要。本文将详细介绍…

Redis 7.x 系列【12】数据类型之基数统计(HyperLogLog)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 常用命令2.1 PFADD2.2 PFCOUNT2.3 PFMERGE 3. 应用场景 1. 概述 基数表示数…

static关键字在Java中的作用

static关键字在Java中的作用 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨Java中的static关键字及其作用。static是Java中一个非常重要的关…

关于 Qt4Qt5迁移至Qt6出现QDesktopWidget和QApplication::desktop()删除后兼容Qt6 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140036861 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…