Linux进程的虚拟内存
简介
用户进程的虚拟地址空间是Linux的一个重要的抽象:它为每个运行进程提供了同样的系统视图,这使得多个进程可以同时运行,而不会干扰到其他进程内存中的内容。
每个应用程序都有自己的线性地址空间,与所有其他应用程序隔开。
进程的虚拟地址空间
各进程虚拟地址空间起始于0,延伸到TASK_SIEZE-1,其上是内核地址空间。
用户程序只能访问整个地址空间的下半部分,不能访问内核部分。如果没有预先达成“协议”,用户进程也不可能操作另外一个进程的地址空间,因为后者的地址空间对前者不可见。
虚拟地址空间由许多不同的段组成,用于不同的目的。
进程地址空间布局
系统中的各个进程都具有一个struct mm_struct的实例,它可以通过 task_struct 访问。这个实例保存了进程的内存管理信息。
<mm_types.h>
struct mm_struct {
...
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
...
unsigned long mmap_base; /* mmap区域的基地址 */
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;
...
}
可执行代码占用的虚拟地址空间,开始和接收分别通过start_code和end_code标记。
初始化数据区域用start_data 和 end_data 标记。
堆的起始地址保存在start_brk, brk表示堆区域当前的结束区域。堆的起始地址在进程生命周期中是不变的,但是堆长度会发生改变,因而brk的值也会变。
参数列表位置arg_start和arg_end, 环境变量 env_start 和 env_end 描述。
mmap_base 表示虚拟地址空间中用于内存映射的起始地址。
task_size 存储了对于进程的地址空间长度。该值通常是TASK_SIZE。
用于内存映射的区域起始于mm_struct->mmap_base,通常设置为TASK_UNMAPPED_BASE。
使用load_elf_binary 载入一个ELF二进制文件时,将创建进程的地址空间。
内存映射的原理
由于所有用户进程中的虚拟地址空间比可用的物理地址内存大得多,因此只有最常用的部分才与物理内存帧关联。
内核必须提供数据结构,以建立虚拟地址空间的区域和相关数据所在位置之间的关联。
内核利用 address_space 数据结构,提供一组方法从后备存储器读取数据。例如,从文件系统读取。因此address_space形成了一个辅助层,将映射的数据表示为连续的线性区域,提供给内存管理子系统。
按需分配和填充页称之为按需调页法(demand paging)。一般步骤:
- 进程试图访问用户地址空间中的一个内存地址,但使用页表无法确定物理地址(物理内存中
没有关联页)。 - 处理器接下来触发一个缺页异常,发送到内核。
- 内核会检查负责缺页区域的进程地址空间数据结构,找到适当的后备存储器,或者确认该访
问实际上是不正确的。 - 分配物理内存页,并从后备存储器读取所需数据填充。
- 借助于页表将物理内存页并入到用户进程的地址空间,应用程序恢复执行
数据结构
前面我们知道,struct mm_struct 很重要,该结构提供了进程在内存布局的所有必要信息。另外,它还包括下列成员,用于管理用户进程在虚拟地址空间中的所有内存区域。
<mm_types.h>
struct mm_struct {
struct vm_area_struct *mmap; /* 虚拟内存区域列表 ,表示虚拟内存*/
struct rb_root mm_rb;
struct vm_area_struct *mmap_cache; /* 上一次find_vma的结果 */
...
}
每个区域都通过一个 vm_area_struct 实例描述,进程的各区域按两种方法排序。
(1) 在一个单链表上(开始于 mm_struct->mmap )。
(2) 在一个红黑树中,根结点位于 mm_rb
红黑树用于扫描特定节点很高效。通过红黑树管理,就可以加快扫描速度。
增加新区域时,内核首先搜索红黑树,找到刚好在新区域之前的区域。因此,内核可以向树和线性表增加新的区域,而无需扫描链表。
地址空间
文件的内存映射可以认为是两个不同的地址空间之间的映射,用来简化系统的工作。一个地址空间是用户进程的虚拟地址空间,另一个就是文件系统所在的地址空间。
内核创建一个映射时,必须建立两个地址空间之间的关联,以支持二者以请求读写的形式通信。
vm_operations_struct 结构用于完成该工作。它提供了一个操作,来读取已经映射到虚拟地址空间,但是其内容尚未进入物理内存的页。
各种不同文件类型(普通文件,设备文件等),以及映射类型和性质相关的信息,还会用到另外一个结构 address_space 。
内存映射
建立映射时内核和应用程序之间的交互,c标准库提供了mmap函数建立映射。
在内核这端,提供了2个系统函数mmap和mmap2。mmap 和 mmap2 之间的差别在于偏移量的语义( off )。在这两个调用中,它都表示映射在文件中开始的位置。对于 mmap ,位置的单位是字节,而 mmap2 使用的单位则是页( PAGE_SIZE )。因此即使文件比可用地址空间大,也可以映射文件的一部分
堆的管理
堆是进程中用于动态分配变量和数据的内存区域。它的实现依赖标准库提供的辅助函数(比如malloc)来分配任意长度内存区域。堆是一个连续的内存区域,在扩展时自下向上增长。前面提到的mm_struct 结构,包含了堆在虚拟地址空间的起始位置和当前结束地址(start_brk和brk)。
<mm_types.h>
struct mm_struct
{
...
unsigned long start_brk, brk, start_stack;
...
};
brk 系统调用只需要一个参数,用于指定堆在虚拟地址空间中新的结束地址(如果堆将要收缩,
当然可以小于当前值)。brk 系统调用实现的入口是 sys_brk 函数。
Linux进程的虚拟内存的更多相关文章
- Linux进程的虚拟内存区域划分
Linux进程的虚拟内存区域分为:代码区.只读常量区.全局区.BSS段.堆区.栈区 代码区:存储功能代码,函数名所在的区域 只读常量区:存放字符串常量,以及const修饰的全局变量 全局区/数据区:存 ...
- linux 进程地址空间的一步步探究
我们知道,在32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G-4G是内核空间.其实,这个4G的地址空间是不存在的,也就是我们所说的虚拟内存空间. 那虚拟内存空间 ...
- linux进程虚拟地址空间
转载源 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块.在Linux系统中, ...
- UNIX环境高级编程——Linux进程地址空间和虚拟内存
一.虚拟内存 分段机制:即分成代码段,数据段,堆栈段.每个内存段都与一个特权级相关联,即0~3,0具有最高特权级(内核),3则是最低特权级(用户),每当程序试图访问(权限又分为可读.可写和可执行)一个 ...
- Linux进程地址空间与虚拟内存
http://blog.csdn.net/xu3737284/article/details/12710217 32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G ...
- Linux进程地址空间和虚拟内存
一.虚拟内存 先来看一张图(来自<Linux内核完全剖析>),如下: 分段机制:即分成代码段,数据段,堆栈段.每个内存段都与一个特权级相关联,即0~3,0具有最高特权级(内核),3则是最低 ...
- Linux进程管理及while循环
目录 进程的相关概念 进程查看及管理工具的使用 Linux系统作业控制 调整进程优先级 网络客户端工具 bash之while循环 20.1.进程类型 守护进程 daemon,在系统引导过程中启动的进程 ...
- Linux进程管理
一.进程管理简介 进程是正在执行的程序或命令,每一个进程都是一个运行实体,都有自己的地址空间,并占用一定的系统资源. 进程管理的作用: 1.判断服务器的健康状态 2.查看系统中的所有进程 3.杀死进程 ...
- 12个Linux进程管理命令介绍(转)
12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国 作者:Linux [字体:大 中 小] 执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...
随机推荐
- wireshark 抓usb包
https://www.freebuf.com/articles/system/96216.html https://blog.csdn.net/shiailan/article/details/97 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]
[易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理] 实用知识 错误处理 我们今天来讲讲Rust中的错误处理. 很多语言都有自己的错误处理方式,比如,java是异常处理机制. ...
- 从Buck-Boost到Flyback
电源的拓扑有很多种,但是其实我们能够理解一种拓扑,就可以理解其他拓扑结构.因为组成各种拓扑的基本元素是一样的. 对于隔离电源.大家接触最多的电路拓扑应该是 flyback. 但是大家一开始做电源的时候 ...
- MYSQL数据库事务4种隔离级别及7种传播行为
事务的特性: 原子性:事务的不可分割,组成事务的各个逻辑单元不可分割. 一致性:事务执行的前后,数据完整性保持一致. 隔离性:事务执行不应该受到其他事务的干扰. 持久性:事务一旦结束,数据就持久化到数 ...
- 【BZOJ 3682】Phorni
题目链接 题目描述 Phorni 是一个音之妖精,喜欢在你的打字机上跳舞. 一天,阳光映射到刚刚淋浴过小雨的城市上时,Phorni 用魔法分裂出了许多个幻影,从 1 到 n 编号. 她的每一个幻影都站 ...
- 语法注释格式;格式化输出;input在py2和py3中的区别;数据的基本类型;运算符;
一.Python中的注释 Python的注释是代码的评论,是让代码让人能更加清晰明确.代码的注释可分为单行注释和多行注释,单行注释用“#”,多行注释用三对单引号或者三对双引号来表示. ps:# 单行注 ...
- 关于C#的学习
长期以来对C#的认识一直停留在微软件开发的完全面向对象的语言的模糊印象上,对其工程也缺乏多文件以上级别的修改能力,而当前流行度的驱使下,想深入了解它并运用. 于是从git上下载了一个C#开源项目,打开 ...
- B/S结构下上传下载大文件(1G以上)的解决方案
以ASP.NET Core WebAPI 作后端 API ,用 Vue 构建前端页面,用 Axios 从前端访问后端 API ,包括文件的上传和下载. 准备文件上传的API #region 文件上传 ...
- Makefile样例
Makefile1 src = $(wildcard ./*cpp) obj = $(patsubst %.cpp, %.o,$(src)) target = test $(target) : $(o ...
- vue自定义组件(vue.use(),install)+全局组件+局部组件
相信大家都用过element-ui.mintui.iview等诸如此类的组件库,具体用法请参考:https://www.cnblogs.com/wangtong111/p/11522520.html ...