虚拟地址空间


前言

准备开始更新博客了,博主是c++后端方向的,目前准备先总结下之前的知识,防止忘记,就先从Linux虚拟地址空间开始吧。

一、虚拟地址空间是什么?

Linux操作系统为每个进程维护了一个虚拟地址空间,对于32位操作系统来说,虚拟地址空间大小为4G,其中包括1G的内核空间和3G的用户空间(如下图)。需要说明的是,虚拟地址空间并不是内存中真实存在的物理地址,而是对物理地址的映射,虚拟地址空间中的虚拟地址虽然是连续的,但对应的物理地址不一定是连续的。
虚拟地址空间_第1张图片

二、用户空间和内核空间

内核空间和用户空间有着不同的权限,我们平时写的应用程序都是运行在用户空间中的,而内核空间主要负责进程资源的分配,用户空间的进程是无法直接访问内核空间的,但可以通过操作系统提供的系统调用来访问内核空间。

1.用户空间

应用程序运行在用户空间中,下图画出了与编程相关的区域分布(但不是全部),下面针对编程相关的内容对每个区进行解释。

-Stack:栈区,应用程序申请的临时变量都存放在栈上,栈区的内存是由编译器管理的,离开了变量的作用域,变量对应的内存就会被释放,不需要程序手动释放。栈区下面的空白是预留给栈分配的空间,默认未8M,超过这个大小时,就会抛出stack overflow。
虚拟地址空间_第2张图片

  • Memory mapping:动态链接区,在程序编译的最后一个阶段,如果需要链接动态库,就会将动态库链接到memory mapping区域,除此之外,使用mmap函数也可以将其他地址映射到动态链接区。
  • Heap:堆区,程序使用new或malloc申请的内存在堆上分配,堆区的内存是需要应用程序手动管理的,并且new/delete和malloc/free必须严格对应,否则可能导致内存泄漏。
  • .bss:.bss段主要放置程序中未初始化的全局变量。
  • .data:.data段用于放置初始化完成的全局变量和局部静态变量,.data段还包括.rodata段,即read only data,用于存放const 修饰的全局变量。
  • .text:又叫代码区,存放程序编译后生成的符号表,通俗点说就是代码,当程序需要调用函数时,都可以从.text段找到要执行的代码。
    运行在用户空间的进程是无法直接访问内核空间的,如果需要访问内核空间,可以通过操作系统提供的系统调用访问内核空间,常见的系统调用包括read()、write()等文件操作函数和申请内存使用的brk()等。

2.内核空间

内核空间相对比较复杂,这里我们仅讨论内核空间中的PCB控制块。PCB控制块,有些文章中也称为task_struct,保存了进程ID,用户ID和组ID,进程状态,文件描述符表,和文件权限等信息,是进程控制和调度的关键。
虚拟地址空间_第3张图片
从上图可以看到,文件描述符表的大小为1024,而前三个默认分别是标准输入、输出和异常,当然应用程序可以使用dup2()函数替换文件描述符。文件描述符的分配是从上至下依次分配的,默认最大值为1024,通过Linux指令ulimit 可以查看或修改文件描述符数量上限,lsof -p + pid号可以查看指定Pid号进程打开的文件描述符。

虚拟地址空间的优点

  1. 虚拟地址空间封装了物理地址,为应用程序提供了统一的接口,方便了编程人员对地址的管理。
  2. 虚拟地址空间隔离了不同进程的地址,如果没有这种隔离,指针可以访问其他进程中的地址,这将是十分危险的。

总结

理解虚拟地址空间是非常重要的,c++的内存管理、对象模型等都离不开虚拟地址空间,操作系统的资源分配、上下文切换和进程调度都依赖于虚拟地址空间。在本文对虚拟地址空间大致理解的基础上,后续将以虚拟地址空间为基础,对c++和操作系统的知识进行整理。

你可能感兴趣的:(linux,c++基础,c++,后端,linux)