进程的家园:探索 Linux 地址空间的奥秘

devtools/2025/1/18 16:05:54/

个人主页:chian-ocean

文章专栏-Linux

前言:

进程地址空间是操作系统为每个进程提供的一块独立的虚拟内存空间。每个进程的地址空间是独立的,确保了一个进程的运行不会直接影响其他进程的内存空间。

在这里插入图片描述

进程地址空间

进程地址空间是操作系统为每个进程分配的一块独立的、连续的虚拟内存地址范围。每个进程在运行时使用自己的地址空间来管理程序的代码、数据、堆栈等内存部分。通过这种机制,操作系统实现了进程之间的内存隔离和资源保护。

核心概念

  1. 虚拟内存
    • 进程地址空间基于虚拟内存机制实现。虚拟内存是操作系统为每个进程提供的一个逻辑内存空间,独立于实际的物理内存。
    • 进程访问的地址是虚拟地址,通过内存管理单元(MMU)和操作系统,将虚拟地址映射到物理内存。
  2. 独立性
    • 每个进程有独立的地址空间,一个进程无法直接访问另一个进程的地址空间。
    • 即使多个进程使用相同的虚拟地址,这些地址也映射到不同的物理内存区域。
  3. 按需分配
    • 地址空间中的很多部分在进程运行时按需分配,比如堆空间和栈空间,操作系统动态管理这些区域。
  4. 分段和分页
    • 地址空间被划分为不同的段(如代码段、数据段、堆和栈)。
    • 使用分页机制,将这些段划分为固定大小的页,以便更高效地管理内存。

在这里插入图片描述


进程地址空间的组成

进程地址空间的组成通常包括以下部分:

  1. 代码段(Text Segment):存放程序的可执行代码(指令),通常只读。
  2. 数据段(Data Segment):存放已初始化的全局变量和静态变量。
  3. BSS段(Block Started by Symbol):存放未初始化的全局变量和静态变量,默认初始化为 0。
  4. 堆(Heap):用于动态分配内存(如 mallocnew),从低地址向高地址增长。
  5. 栈(Stack):存放函数调用信息(如局部变量、函数参数和返回地址),从高地址向低地址增长。
  6. 共享库(Shared Libraries):动态链接库(如 .so.dll 文件)的加载区域。
  7. 内核空间(Kernel Space):保留给操作系统内核使用,用户进程无法直接访问。

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>int g_val_1;
int g_val_2 = 20;int main()
{const char *str = "hello linux";char *p1 = (char*)malloc(100);char *p2 = (char*)malloc(100);int a = 1;int b = 2;                    //代码区                      printf("code addr : %p\n",main);//字符常量区  printf("read only string addr : %p\n",str);//已初始化全局数据区(数据段)                                                                printf("init global value addr : %p\n",&g_val_1);//未初始化全局数据区                       printf("uninit global value addr : %p\n",&g_val_2);//堆区                                           printf("heap addr : %p\n",p1);printf("heap addr : %p\n",p2);                     //栈区printf("stsck addr : %p\n",&a);printf("stsck addr : %p\n",&b);return 0;
}

代码解析

程序内存区域的划分
  1. 代码段 (Text Segment)
    • main 函数所在的地址属于代码段,存放程序的可执行指令。
    • 打印 main 函数的地址,展示代码段所在位置。
  2. 只读常量区 (Read-Only Data Segment)
    • 字符串常量 "hello linux" 被存储在只读常量区。
    • 打印字符串的地址,展示它在内存中的存储位置。
  3. 全局数据段 (Global Data Segment)
    • 已初始化的全局变量(g_val_2)存放在已初始化全局数据区
    • 未初始化的全局变量(g_val_1)存放在BSS 段,程序运行时会自动将其初始化为零。
  4. 堆区 (Heap)
    • 使用 malloc 动态分配的内存存储在堆区,堆区的内存地址通常随着分配逐渐向高地址增长。
    • 打印 p1p2 的地址,展示它们在堆区的存储位置。
  5. 栈区 (Stack)
    • 局部变量(如 ab)存放在栈区,栈的内存地址通常随着分配逐渐向低地址增长(栈是向下生长的)。
    • 打印 ab 的地址,展示栈的内存布局。
示例输出

假设程序运行在 64 位系统上,输出可能类似如下(具体地址会因运行环境而异):

code addr : 0x4005d6                   // 代码段地址
read only string addr : 0x600e10       // 字符常量区地址
init global value addr : 0x601044      // 已初始化全局变量地址(数据段)
uninit global value addr : 0x601040    // 未初始化全局变量地址(BSS 段)
heap addr : 0x7f98c00008c0             // 堆区地址
heap addr : 0x7f98c00008f0             // 堆区地址(连续分配)
stsck addr : 0x7fff5d6b4cfc            // 栈区地址
stsck addr : 0x7fff5d6b4cf8            // 栈区地址
关键点解析
  1. 代码段 (Text Segment)
    • 打印 main 函数的地址,说明它位于代码段中。
  2. 只读常量区 (Read-Only Data Segment)
    • str 指向字符串常量 "hello linux",存储在只读区域(防止被修改)。
  3. 全局数据段 (Global Data Segment)
    • g_val_2 是已初始化的全局变量,存储在数据段中。
    • g_val_1 是未初始化的全局变量,存储在 BSS 段中,并在运行时自动初始化为 0
  4. 堆区 (Heap)
    • malloc 动态分配的内存位于堆区,连续调用 malloc 会分配到地址更高的堆区位置。
  5. 栈区 (Stack)
    • 栈区存储局部变量,变量 ab 的地址说明栈是从高地址向低地址分配的。

典型进程地址空间布局

内存区域描述
内核空间位于地址空间的最高部分,供操作系统内核使用,用户进程不能直接访问。
用户空间用户进程可访问的地址空间,通常由以下部分组成:
栈区(Stack)- 存储局部变量、函数调用信息(如返回地址、参数等)。 - 栈从高地址向低地址生长。
堆区(Heap)- 存储动态分配的内存(如 mallocnew)。 - 堆从低地址向高地址生长。
BSS 段- 存储未初始化的全局变量和静态变量。 - 在程序启动时自动初始化为 0。
数据段(Data)- 存储已初始化的全局变量和静态变量。
代码段(Text)- 存储程序的可执行代码(只读区域)。
只读常量区- 存储字符串常量、只读数据等。

虚拟内存和物理内存(实际内存)的交互

虚拟内存和物理内存之间通过地址映射机制联系在一起。这个映射是通过**页表(Page Table)**完成的。

地址转换过程:

  1. 虚拟地址(Virtual Address):
    • 程序访问的地址,通常分为多个逻辑段(代码段、堆、栈等)。
  2. 页表(Page Table):
    • 页表保存虚拟地址到物理地址的映射关系。
    • 每个进程都有一个独立的页表,由操作系统维护。
  3. 物理地址(Physical Address):
    • 通过页表,虚拟地址被转换为物理地址,从而定位到物理内存中的数据。

虚拟内存与物理内存的差异

特性虚拟内存物理内存
定义操作系统提供的逻辑地址空间计算机硬件提供的实际内存
容量可以比物理内存大,依赖磁盘扩展受限于实际 RAM 的大小
性能速度较慢,部分数据存放在磁盘速度快,直接存储在 RAM 中
隔离每个进程的虚拟地址空间独立共享物理内存,需要通过虚拟内存管理
保护防止进程之间相互访问或破坏无保护,需依赖操作系统管理

页表 (Page Table)

页表是操作系统在实现虚拟内存(Virtual Memory)**时的重要数据结构,用于将**虚拟地址(Virtual Address)**映射到**物理地址(Physical Address)

在分页内存管理中,虚拟地址被分为多个固定大小的块,称为页(Page);物理内存也被分为同样大小的块,称为页框(Page Frame)。页表记录了每个虚拟页与物理页框之间的对应关系。

页表的作用
  • 地址转换:将虚拟地址映射为物理地址。
  • 内存隔离:为每个进程提供独立的地址空间,不同进程的页表互相独立。
  • 内存保护:通过页表中的属性位(如读/写权限)限制对内存的非法访问。
页表的结构
页表的结构

页表是一张映射表,其中每一项称为页表项(Page Table Entry, PTE),记录虚拟页与物理页框之间的映射关系。

页表项 (PTE) 的内容

一个页表项通常包含以下信息:

  1. 页框号(Frame Number):

    • 表示虚拟页映射到的物理页框。
  2. 有效位(Valid Bit):

    • 指示该虚拟页是否有效(是否在物理内存中)。
  3. 读/写权限位(R/W Bit):

    • 表示该页是否可读、写或执行。在物理内存中是无法实现字符常量区在内存中不更改的。由于页表权限位的存在,使我们不能访问此常量区。
    #include<stdio.h>
    int main()
    {char *str = "hello bit";*str = 'H';return 0;
    }
    
    • 示例中,页表中权限位的存在不可更改。
  4. 存在位(Present Bit):

    • 表示该页是否已被加载到物理内存。
    • 如果没有被预加载到内存会触发缺页中断
  5. 页号(Page Number):

    • 表示虚拟页的编号。
  6. 其他控制信息:

    • 如缓存禁用位、页面脏位(是否被修改过)、访问位等。

示例图:

~

  • 示例中,页表中权限位的存在不可更改。
  1. 存在位(Present Bit):

    • 表示该页是否已被加载到物理内存。
    • 如果没有被预加载到内存会触发缺页中断
  2. 页号(Page Number):

    • 表示虚拟页的编号。
  3. 其他控制信息:

    • 如缓存禁用位、页面脏位(是否被修改过)、访问位等。

示例图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


http://www.ppmy.cn/devtools/151601.html

相关文章

鸿蒙中选择地区

1.首页ui import { CustomDialogExampleSelectRegion } from ./selectRegion/SelectRegionDialog;Entry Component struct Index {State selectedRegion: string 选择地区// 地区dialogControllerSelectRegion: CustomDialogController | null new CustomDialogController({b…

Docker详解与部署微服务实战

2013年发布至今&#xff0c; Docker 一直广受瞩目&#xff0c;被认为可能会改变软件行业。 但是&#xff0c;许多人并不清楚 Docker 到底是什么&#xff0c;要解决什么问题&#xff0c;好处又在哪里&#xff1f;今天就来详细解释&#xff0c;帮助大家理解它&#xff0c;还带有…

uniapp 页面铺满屏幕

看了很多帖子&#xff0c;我在最外层加了样式 .personal-center { display: flex; flex-direction: column; width: 100%; height: 100%; background-color: #ffffff; } 结果依然没法铺满全屏&#xff0c;最下面总是多了一段&#xff0c;气不过给 width: 100%; height: 100%;…

ideal jdk报错如何解决

例如: 可能一:环境变量中未配置 请在Path中加入并将要使用的最好置顶,如 可能二:项目结构中语言级别错误: 可能三:Maven工程中,对于模块要单独设置jdk: 如: 未设置则为默认,在博主本次展示中为:

《CPython Internals》阅读笔记:p177-p220

《CPython Internals》学习第 11天&#xff0c;p177-p220 总结&#xff0c;总计 44 页。 一、技术总结 1.memory allocation in C (1)static memeory allocation Memory requirements are calculated at compile time and allocated by the executable when it starts. (2…

C语言特殊操作符

文章目录 前言一、操作符1. **条件&#xff08;三元&#xff09;操作符** ? :&#xff1a;2. **逗号操作符** ,&#xff1a;3. **位域&#xff08;bit-field&#xff09;**&#xff1a;4. **取地址与间接访问操作符** & 和 *&#xff1a;5. **强制类型转换&#xff08;Cas…

Linux的常用命令(一)

目录 一、文件处理命令 1.文件处理命令ls 2.文件处理命令cd 3.文件处理命令pwd 4.文件处理命令touch 5.文件处理命令mkdir 6.文件处理命令cp 7.文件处理命令mv 8.文件处理命令rm 9.文件处理命令cat 10.文件处理命令more 11.文件处理命令head 12.文件处理命令tail …

【STM32-学习笔记-10-】BKP备份寄存器+时间戳

文章目录 BKP备份寄存器Ⅰ、BKP简介1. BKP的基本功能2. BKP的存储容量3. BKP的访问和操作4. BKP的应用场景5. BKP的控制寄存器 Ⅱ、BKP基本结构Ⅲ、BKP函数Ⅳ、BKP使用示例 时间戳一、Unix时间戳二、时间戳的转换&#xff08;time.h函数介绍&#xff09;Ⅰ、time()Ⅱ、mktime()…