[操作系统] 深入进程地址空间

news2025/1/27 12:10:52

程序地址空间回顾

在C语言学习的时,对程序的函数、变量、代码等数据的存储有一个大致的轮廓。在语言层面上存储的地方叫做程序地址空间,不同类型的数据有着不同的存储地址。

下图为程序地址空间的存储分布和和特性:

使用以下代码来验证一下各个类型的是数据存储是否如图所示:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_unval; 
int g_val = 100; 

int main(int argc, char *argv[], char *env[])
{
    const char *str = "helloworld";
    printf("code addr: %p\n", main);
    printf("init global addr: %p\n", &g_val);
    printf("uninit global addr: %p\n", &g_unval);

    static int test = 10;
    char *heap_mem = (char*)malloc(10);
    char *heap_mem1 = (char*)malloc(10);
    char *heap_mem2 = (char*)malloc(10);
    char *heap_mem3 = (char*)malloc(10);
    printf("heap addr: %p\n", heap_mem);
    printf("heap addr: %p\n", heap_mem1);
    printf("heap addr: %p\n", heap_mem2);
    printf("heap addr: %p\n", heap_mem3);

    printf("test static addr: %p\n", &test);
    printf("stack addr: %p\n", &heap_mem);
    printf("stack addr: %p\n", &heap_mem1);
    printf("stack addr: %p\n", &heap_mem2);
    printf("stack addr: %p\n", &heap_mem3);

    printf("read only string addr: %p\n", str);
    for(int i = 0; i < argc; i++)
    {
        printf("argv[%d]: %p\n", i, argv[i]);
    }
    for(int i = 0; env[i]; i++)
    {
        printf("env[%d]: %p\n", i, env[i]);
    }

    return 0;
}

结果如下:

$ ./a.out
code addr: 0x40055d // 正文代码 main()
init global addr: 0x601034 // 未初始化全局变量
uninit global addr: 0x601040 // 初始化的全局变量
heap addr: 0x1791010 // 堆:向上增长 ↑
heap addr: 0x1791030
heap addr: 0x1791050
heap addr: 0x1791070
test static addr: 0x601038 // static int 类型
stack addr: 0x7ffd0f9a4368 // 栈:向下增长 ↓
stack addr: 0x7ffd0f9a4360
stack addr: 0x7ffd0f9a4358
stack addr: 0x7ffd0f9a4350
read only string addr: 0x400800 // const char *str
argv[0]: 0x7ffd0f9a4811
env[0]: 0x7ffd0f9a4819
env[1]: 0x7ffd0f9a482
env[2]: 0x7ffd0f9a4845
env[3]: 0x7ffd0f9a4850
env[4]: 0x7ffd0f9a4860
env[5]: 0x7ffd0f9a486e

通过地址结果的验证可以明确:

  1. 堆向上增长,随着创建申请空间,空间地址逐渐变大。
  2. 栈向下增长,随着创建变量,变量空间地址逐渐变小。
  3. const char*的最字符串常量地址与正文代码的地址相近,说明在编译的时候会将该类型硬编到正文代码中,所以形成了代码只可读。
  4. 函数内部的static类型的变量地址与初始化数据中全局变量地址相近,因为static类型的变量在编译时就会在初始化数据区域,所以就会作为全局变量。则static是全局属性。

虚拟地址

实际上程序的地址空间是内存吗?

地址空间不是内存地址,而是虚拟地址!

在语言层上,我们会叫做程序地址空间。但是在系统层面上,会将其称为进程地址空间或者虚拟地址空间

可以通过以下代码来验证:

#include<stdio.h>
#include <unistd.h>

int gval = 100;

int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        while (1)
        {
            printf("子: gval: %d, &gval: %p, pid: %d, ppid: %d\n", gval, &gval, getpid(), getppid());
            sleep(1);
            gval++;
        }
    }
    else
    {
        while (1)
        {
            printf("父: gval: %d, &gval: %p, pid: %d, ppid: %d\n", gval, &gval, getpid(), getppid());
            sleep(1);
        }
    }
    return 0;
}

结果如图:

父子进程按照代码逻辑进行运行,随着每次睡眠过后子进程的全局变量就会+1。但是通过结果可以发现,父进程和子进程各自的全局变量地址都是0x601054。明明是同一个地址空间,为什么全局变量gval的值不同呢?

这就证明了,进程的地址空间一定不是内存地址,不是物理上的地址,而是虚拟地址!我们在程序中使用指针指向的地址,以及取变量地址等操作,实际上都是在访问虚拟地址。

物理地址一般不会向用户展示,操作系统会将虚拟地址转化成物理地址,虚拟地址由操作系统统一管理。

进程地址空间

基础概念

每个进程都有其虚拟地址空间mm_struct和页表存在于task_struct中,每个地址空间1字节。所以对于32位的机器,在虚拟地址空间中共有2^32个地址空间,64位机器则有2^32地址空间。

页表中存储的是虚拟地址和物理地址的映射关系。

程序在运行时实际上管理的是虚拟地址空间中的地址,当程序需要进行管理一个地址的时候,操作系统会将该地址在页表中进行查找,就可以得到与其对应映射的物理内存地址。然后操作系统会对物理内存地址的数据进行访问管理。

子进程会继承父进程的虚拟地址空间和页表。

如何通过一个字节地址访问多个字节大小的数据?

通过地址和类型偏移量确定整个数据。

假设存在一个int变量a,当我们通过虚拟地址空间的映射找到物理地址后,会通过int类型在结构体中的偏移量进行确定整个数据内容,因为所有的数据都是通过先描述后组织进行管理,通过对应的数据结构就可以确定数据的位置。

进程如何独立

子进程的虚拟地址空间和页表会继承父进程,那么进程之间是怎么独立的呢?

假设父进程存在一全局变量int g_val,在当前父进程虚拟地址在页表中已经与物理地址映射。然后创建子进程,当子进程中尝试对g_val修改时操作系统会进行以下操作:

  1. 在物理地址空间中会重新开辟一块int大小的空间,在此空间内存储修改后的地址。
  2. 在页表中查询子进程虚拟地址空间中g_val虚拟地址,然后将新开辟的物理地址与虚拟地址重新建立映射关系。
  3. 此时,因为继承的关系,父进程与子进程中的g_val使用的是同一个虚拟地址,但由于子进程对g_val进行修改,所以同一个变量的虚拟地址映射的是不同的物理地址。

这就是写时拷贝的机制!!

所以发生写时拷贝后,子进程对于修改的数据会重新构建映射关系,而其他的数据、代码、变量等都是共享的物理资源,这也避免了重复拷贝的内存的浪费,减少创建时间。

**通过这种机制就形成了进程的独立! **

虚拟地址与进程地址空间关系

通过上文可知,对于32位的机器来说,每个进程的虚拟地址空间有2^32字节的大小,也就是4个G。但如果整个内存只有4G的话,那么一个进程就要把所有的内存空间全部占满吗?显然不可能。

如其名,虚拟地址空间并不是真正的内存空间。操作系统会让每个进程都认为他们可以独占物理内存,但是在实际使用的时候会根据真实的需求通过映射关系开辟内存空间。

虚拟地址空间如何从物理内存划分

由于进程不会独占物理内存,那么肯定有相对应的划分管理方法。

虚拟地址的本质:结构体对象,数据结构!

  • mm_struct 中存储的起始地址和结束地址用 int 表示。
  • 每个区域的范围是 [start_address, end_address],这些地址用 int 记录下来。例如:
struct mm_struct {
    int code_start;   // 代码段起始地址
    int code_end;     // 代码段结束地址
    int heap_start;   // 堆起始地址
    int heap_end;     // 堆结束地址
    int stack_start;  // 栈起始地址
    int stack_end;    // 栈结束地址
};

虚拟地址通常是用 int(4字节,32位) 类型存储的,而每个 int 值就直接对应一个地址。虚拟地址空间中的地址可以用一个 int 值表示,因为 int 的取值范围足够覆盖整个虚拟地址空间的范围(0 ~ 232−12^{32} - 1232−1,即 4GB)。

在32为机器中虚拟地址由2^32字节空间,每个区域(栈,堆…)都有自己确定的区域,然后堆所有的区域进行编址。虚拟地址空间就是结构体mm_struct,里面存放的就是每个区域的起始地址和结束地址对应的int值。

区域调整

既然每个区域的大小是用int值进行确定,那么当需要对这个区域大小进行调整的时候,区域调整就是对起始和结束的整数范围进行调整。

根据各个区域的特性,例如堆向上增长,栈向下增长,将其对应的startend进行+或者,以此来进行区域的调整。

小结:虚拟地址空间是什么

操作系统需要对进程中的虚拟地址空间进行管理,虚拟地址空间是内核中的一种数据结构mm_struct大部分属性都是各个区域的开始和结束地址的int值。

先描述,在组织。作为数据结构,操作系统不仅会对进程进行管理,也会对mm_struct进行管理,用链表进行管理。但是实际上通过PCB也可以直接访问到mm_struct

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

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

相关文章

OpenCV:图像处理中的低通滤波

目录 简述 什么是低通滤波&#xff1f; 各种滤波器简介与实现 方盒滤波 均值滤波 中值滤波 高斯滤波 双边滤波 各种滤波的对比与应用场景 相关阅读 OpenCV基础&#xff1a;图像变换-CSDN博客 OpenCV&#xff1a;图像滤波、卷积与卷积核-CSDN博客 简述 低通滤波是一…

32、【OS】【Nuttx】OSTest分析(1):stdio测试(二)

背景 接上篇wiki 31、【OS】【Nuttx】OSTest分析&#xff08;1&#xff09;&#xff1a;stdio测试&#xff08;一&#xff09; 继续stdio测试的分析&#xff0c;上篇讲到标准IO端口初始化&#xff0c;单从测试内容来说其实很简单&#xff0c;没啥可分析的&#xff0c;但这几篇…

OpenAI掀桌子!免费版ChatGPT,提供o3-mini模型!

逆天免费用 今天凌晨&#xff0c;OpenAI联合创始人兼首席执行官Sam Altman宣布了一个大消息——免费版ChatGPT&#xff0c;将提供o3-mini模型&#xff01; 网页们纷纷不淡定了 看来OpenAI&#xff0c;这o3-mini还没正式上线呢&#xff0c;就免费开放使用了。 不过还是要感谢…

redis离线安装部署详解(包括一键启动)

像上文一样 因为在学习的过程中没有查到一个详细的离线部署方案 所以在自己学习之后想要自己写一个文章 希望可以帮助后续学习redis离线部署的朋友少走一线弯路 首先就是下载安装包 可以自己在本地下载再传到机器上&#xff08;通过xftp或lrzsz都可&#xff09; http://d…

图论汇总1

1.图论理论基础 图的基本概念 二维坐标中&#xff0c;两点可以连成线&#xff0c;多个点连成的线就构成了图。 当然图也可以就一个节点&#xff0c;甚至没有节点&#xff08;空图&#xff09; 图的种类 整体上一般分为 有向图 和 无向图。 有向图是指 图中边是有方向的&a…

小利特惠源码/生活缴费/电话费/油卡燃气/等充值业务类源码附带承兑系统

全新首发小利特惠/生活缴费/电话费/油卡燃气/等充值业务类源码附带U商承兑系统 安装教程如下 图片:

ESMC-600M蛋白质语言模型本地部署攻略

前言 之前介绍了ESMC-6B模型的网络接口调用方法&#xff0c;但申请token比较慢&#xff0c;有网友问能不能出一个本地部署ESMC小模型的攻略&#xff0c;遂有本文。 其实本地部署并不复杂&#xff0c;官方github上面也比较清楚了。 操作过程 环境配置&#xff1a;CUDA 12.1、…

Java 实现Excel转HTML、或HTML转Excel

Excel是一种电子表格格式&#xff0c;广泛用于数据处理和分析&#xff0c;而HTM则是一种用于创建网页的标记语言。虽然两者在用途上存在差异&#xff0c;但有时我们需要将数据从一种格式转换为另一种格式&#xff0c;以便更好地利用和展示数据。本文将介绍如何通过 Java 实现 E…

Ubuntu20.04 运行 PL-VIO

文章目录 运行后不知为何没有线特征 运行后不知为何没有线特征

centos操作系统上以service形式运行blackbox_exporter监控网页端口

文章目录 前言一、blackbox_exporter是什么二、使用步骤1.获取二进制文件2.准备部署脚本3.执行命令&#xff0c;进行部署4.prometheus中增加需要监控页面的job信息 三、查看部署结果四、配置到grafana中总结 前言 记录一下centos操作系统上以简单的service形式运行blackbox_ex…

Linux内核编程(二十一)USB驱动开发-键盘驱动

一、驱动类型 USB 驱动开发主要分为两种&#xff1a;主机侧的驱动程序和设备侧的驱动程序。一般我们编写的都是主机侧的USB驱动程序。 主机侧驱动程序用于控制插入到主机中的 USB 设备&#xff0c;而设备侧驱动程序则负责控制 USB 设备如何与主机通信。由于设备侧驱动程序通常与…

RV1126画面质量四:GOP改善画质

一&#xff0e; 什么是 GOP GOP 实际上就是两个 I 帧的间隔&#xff0c;比方说分辨率是 1920 * 1080 50 帧&#xff0c;假设 GOP 为 5&#xff0c;那就是大概 2s 插入一个 I 帧。我们再 回顾下&#xff0c;H264/H265 的帧结构。H264/H265 分别分为三种帧类型&#xff1a;I 帧、…

【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文

全球网络犯罪与网络安全政策的多维度分析及效能评估 摘要1 Introduction1.1 Problem Background1.2Restatement of the Problem1.3 Literature Review1.4 Our Work 2 Assumptions and Justifications数据完整性与可靠性假设&#xff1a;法律政策独立性假设&#xff1a;人口统计…

Vivado生成X1或X4位宽mcs文件并固化到flash

1.生成mcs文件 01.在vivado里的菜单栏选择"tools"工具栏 02.在"tools"里选择"生成内存配置文件" 03.配置参数 按照FPGA板上的flash型号进行选型&#xff0c;相关配置步骤可参考下图。 注意&#xff1a;Flash数据传输位宽如果需要选择X4位宽&am…

idea plugin插件开发——入门级教程(IntelliJ IDEA Plugin)

手打不易&#xff0c;如果转摘&#xff0c;请注明出处&#xff01; 注明原文&#xff1a;idea plugin插件开发——入门级教程&#xff08;IntelliJ IDEA Plugin&#xff09;-CSDN博客 目录 前言 官方 官方文档 代码示例 开发前必读 Intellij、Gradle、JDK 版本关系 plu…

Linux的常用指令的用法

目录 Linux下基本指令 whoami ls指令&#xff1a; 文件&#xff1a; touch clear pwd cd mkdir rmdir指令 && rm 指令 man指令 cp mv cat more less head tail 管道和重定向 1. 重定向&#xff08;Redirection&#xff09; 2. 管道&#xff08;Pipes&a…

docker 简要笔记

文章目录 一、前提内容1、docker 环境准备2、docker-compose 环境准备3、流程说明 二、打包 docker 镜像1、基础镜像2、国内镜像源3、基础的dockerfile4、打包镜像 四、构建运行1、docker 部分2、docker-compose 部分2.1、构建docker-compose.yml2.1.1、同目录构建2.1.2、利用镜…

Windows 与 Linux 文件权限的对比与转换

Windows和Linux在文件权限管理方面存在显著差异。了解它们的对比和转换方法对于跨平台操作和管理文件非常重要。以下是详细的对比和转换方法&#xff1a; 一、Windows 文件权限 1. 权限类型 Windows使用基于用户和组的权限模型&#xff0c;常见的权限类型包括&#xff1a; 读…

FireFox | Google Chrome | Microsoft Edge 禁用更新 final版

之前的方式要么失效&#xff0c;要么对设备有要求&#xff0c;这次梳理一下对设备、环境几乎没有要求的通用方式&#xff0c;universal & final 版。 1.Firefox 方式 FireFox火狐浏览器企业策略禁止更新_火狐浏览器禁止更新-CSDN博客 这应该是目前最好用的方式。火狐也…

华硕笔记本装win10哪个版本好用分析_华硕笔记本装win10专业版图文教程

华硕笔记本装win10哪个版本好用&#xff1f;华硕笔记本还是建议安装win10专业版。Win分为多个版本&#xff0c;其中家庭版&#xff08;Home&#xff09;和专业版&#xff08;Pro&#xff09;是用户选择最多的两个版本。win10专业版在功能以及安全性方面有着明显的优势&#xff…