当前位置: 首页 > news >正文

程序地址空间

文章目录

    • 1. 程序地址空间回顾
    • 2. 虚拟地址
      • 2.1 概念
      • 2.2 虚拟地址与进程地址空间
      • 2.3 区域划分 与 mm_struct
      • 2.4 虚拟地址空间的意义
      • 2.5 一些问题
      • 2.6 堆区
    • 3. 作者的感悟


1. 程序地址空间回顾

我们在讲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); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem2); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem3); //heap_mem(0), &heap_mem(1)printf("test static addr: %p\n", &test); //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("stack addr: %p\n", &heap_mem2); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem3); //heap_mem(0), &heap_mem(1)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;
}

运行结果:
在这里插入图片描述
结论:输出结果同图中结果一样,由代码区到环境变量区的地址逐渐增大。其中注意的点:

  1. 代码段(code):地址为0x40055d,属于程序代码的加载区域,通常在较低地址(符合操作系统对代码段的布局习惯)。
  2. 全局数据段:
  • 初始化全局变量(init global)地址0x601034,
  • 未初始化全局变量(uninit global)地址0x601040,
  • 静态变量(test static)地址0x601038

三者属于全局数据段 ,地址高度集中(都在0x6010xx区间),体现了全局数据的连续存储特性。

  1. 只读字符串段:地址0x400800,属于程序的只读数据区(存储字符串常量),和代码段地址(0x40055d)同属0x400xxx区间,说明只读数据与代码在内存中是相邻 / 同区域管理的。
  2. (Heap)的地址规律
    堆地址为0x1415010、0x1415030、0x1415050、0x1415070——每次地址递增0x20(即十进制的 32),体现了堆内存 “按需分配、向上增长” 的特点:堆由程序动态申请(如malloc),每次分配的内存块地址是连续递增的,且块大小固定。
  3. (Stack)的地址规律
    栈地址为0x7ffdccce9b798、0x7ffdccce9b790、0x7ffdccce9b788、0x7ffdccce9b780——每次地址递减0x8(即十进制的 8),体现了栈== “后进先出、向下增长” ==的核心特性:栈用于函数调用、局部变量存储,每次函数调用或局部变量定义会 “压栈”,地址向低地址方向递减(0x7ffd…属于用户栈的典型地址范围,在 Linux 系统中栈从高地址向低地址扩展)。
  4. 命令行与环境变量的地址规律
    argv[0]地址0x7ffdccce9c7fc,env系列地址从0x7ffdccce9c803开始,且env[0]到env[22]的地址连续递增(每次递增几个字节到十几个字节不等)。
    这体现了命令行参数和环境变量在内存中是连续存储的,且位于栈的附近区域(0x7ffd…属于用户栈 / 参数区的地址范围),符合操作系统对程序启动参数的布局逻辑。

Q: 程序地址空间是内存吗?
A: 不是,是进程地址空间(虚拟地址空间),是系统的概念,而不是语言的概念。
验证一下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int gval = 100;int main()
{printf("父进程开始运行,pid: %d\n", getpid());pid_t id = fork();if(id < 0){perror("fork");return 1;}else if(id == 0){printf("子进程, pid: %d, ppidid: %d, gval: %d, &gval: %p\n", getpid(), getppid(), gval, &gval);// childwhile(1){sleep(1);gval+=10; // 修改printf("子进程, pid: %d, ppidid: %d, gval: %d, &gval: %p\n", getpid(), getppid(), gval, &gval);}}else{//fatherwhile(1){sleep(1);printf("父进程, pid: %d, ppidid: %d, gval: %d, &gval: %p\n", getpid(), getppid(), gval, &gval);}}return 0}

这段代码会发生写时拷贝,因为进程具有独立性。
在这里插入图片描述
Q: 但明明访问的是同一个gval,而且是同一个地址的gval,为什么会显示出不同的值呢?
A: 说明访问的地址根本就不是内存物理地址,而是虚拟地址

2. 虚拟地址

在这里插入图片描述

2.1 概念

  1. 一个进程一个虚拟地址空间。
  • 虚拟地址空间对应的宽度为一字节。
  • 32位地址总有232个地址,共4GB 。
  • 64位地址总有264个地址,共4GB 。
  • 4GB 分为3GB的用户空间和1GB的内核空间,其中用户空间拿着地址就可以直接访问。
  1. 一个进程一个页表。
  • 在创建一个变量时,变量在内存上存在一份,在虚拟地址上也存在一份。
  • 页表的左侧填写的虚拟地址,右侧填写内存的物理地址。
    页表是用来做虚拟地址和物理地址的映射。
  1. 子进程也有自己的页表。
  • 子进程的页表也拷贝自父进程。发生的是简单的浅拷贝。
  • 当子进程对数据进行修改时,操作系统介入,重新开辟一块物理地址给子进程,重新填写页表,但是虚拟地址没有发生改变,这就导致我们看到的实验结果,明明地址一样,却出现不同的值。这就叫做写时拷贝

用户是看不到物理地址的,操作系统将物理地址隐藏起来了。

2.2 虚拟地址与进程地址空间

虚拟地址最大的意义是:操作系统让每一个进程都认为自己在独占物理内存。是画大饼的操作。
在这里插入图片描述

Q: 操作系统给每个进程一个虚拟地址空间,让其认为自己独占物理内存,但实际不是这样,在系统上,可能同时运行多个进程,并且物理地址只有固定的大小。那么操作系统是不是应该将虚拟地址管理起来呢?如果要管理,应该怎么管理?
A: 先描述,再组织。(简直是六字真言)
所以!虚拟地址空间本质就是一个数据结构,结构体变量:mm_struct

2.3 区域划分 与 mm_struct

现在我们知道,虚拟内存被划分为很多区域,这一操作叫做区域划分。而区域划分的关键就是记录地址空间的开始和结束。另一方面要做调整区域的操作,本质上就是调整开始和结束。
描述linux下进程的地址空间的所有的信息的结构体是mm_struct。每个进程只有⼀个mm_struct结构,在每个进程的task_struct结构中,有⼀个指向该进程的mm_struct结构体指针。

struct task_struct{/*...*/struct mm_struct  *mm;              struct mm_struct  *active_mm;              
/*...*/}
struct mm_struct
{/*...*/struct vm_area_struct *mmap;   /* 指向虚拟区间(VMA)链表*/        struct rb_root mm_rb;          /* red_black树 */            unsigned long task_size;       /*具有该结构体的进程的虚拟地址空间的⼤⼩*/    // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;/*...*/
}

由此我们知道了,当加载一个进程时,操作系统在虚拟地址空间中申请指定大小的空间(调整区域划分),然后将程序加载内存上,在内存上申请物理空间,最后进行页表映射。物理地址转换成虚拟地址,虚拟地址供上层使用。

2.4 虚拟地址空间的意义

Q: 为什么要有虚拟地址空间?

A:

  1. 将地址无序变有序,提升内存利用率。
  2. 虚拟地址转换成物理地址,是由操作系统(实际上是硬件)查找页表的映射。
  3. 页表中还有权限管理,在地址转换的过程中,可以对地址和操作的合法性判定,由此实现对物理内存的保护。
  4. 实现缺页中断机制,动态加载进程数据。
  5. 实现进程管理和内存管理一定程度的解耦合。

看个例子:

int main()
{char *str = "hello world";*str = H;return 0;
}

以上代码可以通过编译器编译,但是在运行的时候就会崩溃。
原因是: str为字符串常量,在编译时被硬编译在代码区和初始化数据区之间,因此在页表中就只有读权限,所以在执行第二行代码时,映射页表的过程中,发生权限拦截

2.5 一些问题

  1. 我们可以不加载程序的代码和数据,只有task_struct, mm_struct, 页表。(通过缺页中断处理)
  2. 创建进程时现有task_struct, mm_struct, 等,再有代码和数据。
  3. 理解进程阻塞挂起,操作系统将进程页表的物理地址清空,再将数据唤出到磁盘上,保留虚拟地址,实现挂起。

2.6 堆区

Q: 我在写代码时,动态申请地址,是在堆区上开辟的,那为什么堆区是一整块儿的地址?不止一个堆吧?
A: 是的,存在vm_area_struct *mmap链表,来管理堆区,记录每一个堆的开始与结束。
在这里插入图片描述
实际上,每一个区域都有vm_area_struct ,用来表示该区域的开始和结束。
在这里插入图片描述

3. 作者的感悟

学Linux的感觉很爽,怎么讲?感觉真的在很认真的了解计算机的每一个动作,我从来没有这么认真的交往过一个朋友,Linux你身上的特性,对我来讲不是负担,而是你独一无二的性格。我觉得,我应该能学好Linux。


http://www.dtcms.com/a/530353.html

相关文章:

  • 做企业门户网站网站建好后维护麻烦吗
  • 雅菲奥朗人工智能知识墙分享(三):『AI算力:人工智能时代的“核心引擎”』
  • 如何判断网站是否被收录wordpress加载视频教程
  • 手机版网站系统网站建设和媒体渠道
  • 网站加速 wordpresswordpress媒体库一直转圈
  • Linux 离线迁移conda R虚拟环境教程
  • 过界女主个人做网站的班级网站的建设
  • 杭州公司网站建设套餐怎样申请建立自助网站
  • 网站备案号查询网温州论坛官方网
  • 做网站百度推广南沙网站建设公司哪家好
  • 重庆网站建设推荐网站营销推广有哪些
  • 网站控制做百度网站需不需要备案吗
  • ps制作网站网站开发文档模板下载
  • 手机站建设前端网站设计
  • 门户网站建设招投标php网站视频代码
  • 格尔木网站建设公司怎么入侵网站后台管理
  • 网站升级改造建设方案百度网站地图在线生成
  • 北京建设企业协会网站wordpress 打开慢 google
  • 创建网站投资多少网页生成应用
  • asp.net开发网站和优势网站建设】
  • 怎么计算网站开发费用如何建设自己的网站
  • 云计算网站建设郑州哪家做网站最好
  • 重庆网站建设推广优化医生做兼职有什么网站吗
  • 禅城网站制作设计室内装修app软件
  • 旅游去过的地方可做标识网站一个域名做多个网站
  • 基于ATC89C51单片机的超市临时储物柜密码锁设计
  • 汽车网站建设策划书施工企业安全管理制度
  • 怎么知道网站是php个人手机版网站建设
  • HTML 01入门:从概念到开发环境搭建与页面头部配置
  • 大酒店网站源代码郴州建设网站哪家好