Linux中内存管理详解

article/2025/10/11 17:30:17

Linux中内存管理

内存管理的主要工作就是对物理内存进行组织,然后对物理内存的分配和回收。但是Linux引入了虚拟地址的概念。

虚拟地址的作用

如果用户进程直接操作物理地址会有以下的坏处:
1、 用户进程可以直接操作内核对应的内存,破坏内核运行。
2、 用户进程也会破坏其他进程的运行

CPU中寄存器中存储的是逻辑地址,需要进行映射才能转化为对应的物理地址,然后获取对应的内存。

通过引入逻辑地址,每个进程都拥有单独的逻辑地址范围。

当进程申请内存的时候,会为其分配逻辑地址和物理地址,并将逻辑地址和物理地址做一个映射

所以,Linux内存管理涉及到了以下三个部分:

1、物理内存

物理内存的组织

Linux中内存分为3个级别,从下到上依次为:

1、Page: 一个page的大小为 4k, Page 是内存的一个最基本的单位。

2、Zone: Zone中提供了多个队列来管理page。
Zone分为3种
2.1、 ZONE_DMA: 用来存放DMA读取IO设备的数据,内核专用
2.2、 ZONE_NORMAL:用来存放内核的相关数据,内核专用
2.3、 ZONE_HIGHMEM:高端内存,用来用户进程存放数据

3、Node 节点,一个CPU对应着一个Node,一个Node包括一个Zone_DMA、 ZONE_NORMAL、ZONE_HIGHMEM。

同时当一个CPU对应的内存用光后,可以申请其他CPU对应的内存。

在这里插入图片描述

物理内存的分配

Linux将内存分配分为两种:

1、大内存

大内存利用伙伴系统分配
在这里插入图片描述
伙伴系统的做法是将ZONE中的 Page 分组,然后组装为多个链表。

链表中存放的是 页块 的集合

页块对应着有不同的大小,分别为 1、2、4、8 … 1024个页。

当请求(2i-1 ,2i]大小的 page 的时候,会直接请求2i 个页, 如果对应的链表中有对应的页块,就直接分配。如果对应的链表没有,就往上找 2i+1,如果2i+1存在,就将其分为 2 个 2i 页块,将其中1个2i加入到对应的链表中,将另外一个分配出去。

例如,要请求一个 128 个页的页块时,先检查 128 个页的页块链表是否有空闲块。如果没有,则查 256 个页的页块链表;如果有空闲块的话,则将 256 个页的页块分成两份,一份使用,一份插入 128 个页的页块链表中。如果还是没有,就查 512 个页的页块链表;如果有的话,就分裂为 128、128、256 三个页块,一个 128 的使用,剩余两个插入对应页块链表。

2、小内存分配

小内存分配利用slub分配,比如对象等数据
slub 就是 将几个页单独拎出来 作为缓存,里面维护了链表。每次直接从链表中获取对应的内存,用完之后也不用清空,就直接挂到链表上,然后等待下次利用。

在这里插入图片描述

2、如何组织虚拟地址

虚拟地址对应的是虚拟空间,虚拟空间只不过是一个虚拟地址的集合,用来映射物理内存。
在这里插入图片描述
虚拟空间分为 用户态内核态

32位系统中 将虚拟空间按照 1:3的比例分配给 内核态用户态

64位系统中 分别给 内核态 和 用户态 分配了 128T。

用户态结构
在这里插入图片描述
每个进程 都会 对应一个 用户态虚拟空间, 里面存放了 Text(代码)的内存虚拟地址范围、 Data(数据)的内存虚拟地址范围、BSS(全局变量)的内存虚拟地址范围、堆的虚拟地址范围、栈的虚拟地址范围,以及mmap 内存映射区。

其中mmap 用于申请动态内存的时候的映射,堆和栈都是动态变化的。

一个进程对应的用户态中的 各个方面的虚拟地址信息都通过一个struct 来存储在内存中,当创建进程的时候会为其分配内存存储对应的虚拟地址信息

内核态结构
在这里插入图片描述
Linux中的内核程序 共用一个 内核态虚拟空间。其中分为了以下几部分:

1、直接映射区
896M,内核空间直接映射到对应的ZONE_DMA和ZONE_NORMAL中。为什么叫做直接映射呢? 逻辑地址 直接 减去对应的差值就可以得到对应的物理地址。固定死了。

2、动态映射

为什么要引入动态映射呢?
因为所有物理内存的分配都需要内核程序进行申请,用户进程没有这个权限。所以内核空间一定要能映射到所有的物理内存地址。

那么如果都采用直接映射的话,1G大小逻辑地址的内核空间只能映射1G大小的物理内存。

所以引入了动态映射,动态映射就是 内核空间的逻辑地址可以映射到 物理内存中的ZONE_HIGHMEM(高端内存)中的任何一个地址,并且在对应的物理内存使用完之后,可以再映射其他物理内存地址。

动态映射分为三种
1、动态内存映射: 使用完对应的物理内存后,就可以映射其他物理内存了。

2、永久内存映射: 一个虚拟地址只能映射一个物理地址。如果需要映射其他物理地址,需要解绑。

3、固定内存映射: 只能被某些特定的函数来调用引用物理地址。

动态内存映射和直接映射的区别
动态映射和直接映射的区别就是逻辑地址到物理地址的转化规则。

直接映射

直接映射的规则是死的,一个逻辑地址对应的物理地址是固定的。通过逻辑地址加或者减去一个数,就可以得到对应的物理地址。

动态映射

动态映射是动态的绑定,每个逻辑地址对应的物理地址是动态的,通过页表进行查询

用户空间映射:

用户空间 采用 动态映射,每个虚拟地址可以被映射到一个物理地址,映射到ZONE_HIGHMEM。

为什么用户空间不采用直接映射呢?

因为物理内存是多个进程所有的,每个进程都有一个用户空间。如果采用直接映射的话,对应的物理地址是会冲突的。其用户空间的逻辑地址大小都为3G,所以存在逻辑地址相同,但是对应的物理地址不同。需要通过页表来转化,一个进程会对应一个页表。

3、如何将虚拟地址映射到物理内存

虚拟地址通过 页表虚拟地址 转化为 物理地址

每个进程都对应着一个页表

内核只有一个页表

虚拟空间 和 物理内存 都按照 4k 来分页,一个虚拟空间中的页 和 物理内存中页 是 一一对应的。

页表映射

在这里插入图片描述

如上图所示,将虚拟地址中的页号 通过页表转化为 对应的物理页号,然后通过页内偏移量 就可以得到对应的 物理地址了。

在这里插入图片描述

但是 1个进程就需要一个页表,一个4G的内存条,就需要1M个页表记录来描述,假如 1 个 页表记录需要 4个字节,那么就需要 4MB。而且页表记录是通过下标来对应的,通过虚拟页号来乘以对应的页表项大小来计算得到对应的地址的。

所以Linux将 4M 分为 1K个 4K, 一个4K对应着一个page,用来存储对应的真正的页表记录。将 1K 个 page 分开存放,就不要求连续的4M了。

如果将4M 分成 1K 个离散的 page的话,怎么虚拟地址对应的页表号呢?

利用指针,存储1K个地址,分别指向这1K个page, 地址的大小为4个字节,也就是32位,完全可以表示整个内存的地址范围。

1K * 4个字节,正好是一个page 4k,所以 也就是利用 1个 page来存储对应的页表记录索引。

所以 我们的虚拟地址寻找过程如下:
1、找到对应的页表记录索引位置,因为有1K个索引,所以用10位就可以表示了

2、通过索引可以找到对应的真正的页表地址,对应的有1K个页表记录,所以用10位就可以表示了

3、1个页有4K,通过12位就可以表示其页内偏移量了。

所以虚拟地址被分为了三部分
1、 10位 表示索引偏移
2、 10位 表示页表记录偏移
3、 12位 表示页内偏移

虽然这种方式增加了索引项,进一步增加了内存,但是减少了连续内存的使用,通过离散的内存就可以存储页表。

这是对于32位系统,64位采用了5级页表

在这里插入图片描述

映射流程图
在这里插入图片描述

用户态申请内存时,只会申请对应的虚拟地址,不会直接为其分配物理内存,而是等到真正访问内存的时候,产生缺页中断,然后内核才会为其分配,然后为其建立映射,也就是建立对应的页表项。

TLB

TLB就是一个缓存,放在CPU中。用来将虚拟地址和对应的物理地址进行缓存。
当查询对应的物理地址的时候,首先查询TLB,如果TLB中存在对应的记录,就直接返回。如果不存在,就再去查询页表。

虚拟内存

虚拟内存 指的是 将硬盘中划出一段 swap分区 当作 虚拟的内存,用来存放内存中暂时用不到的内存页,等到需要的时候再从 swap 分区中 将对应的内存页调入到 内存中。 硬盘此时相当于一个虚拟的内存。

从逻辑上能够运行更大内存的程序,因为程序运行的时候并不需要把所有数据都加载到内存中,只需要将当前运行必要的相关程序和数据加载到内存中就可以了,当需要其他数据和程序的时候,再将其调入。

相较于真正的内存加载,虚拟内存需要将数据在内存和磁盘中不断切换,这是一个耗时的操作,所以速度比不上真正的内存加载。

总结

虚拟空间物理内存 都分为 内核空间用户空间

虚拟地址需要通过页表转化为物理地址,然后才能访问。

用户虚拟空间 只能映射 物理内存中的用户内存,无法映射到物理内存中的内核内存,也就是说,用户进程只能操作用户内存。

内核空间 只能被 内核 申请使用,用户进程只能操作用户空间的物理内存和虚拟空间。

当用户进程 调用系统调用的时候,会将其对应的代码和数据运行在内核空间中。

所以当调用 内核空间 读取文件或者网络数据的时候,首先会将数据拷贝到内存空间,然后在将数据从内核空间拷贝到用户空间。因为 用户进程不能访问内核空间。


http://chatgpt.dhexx.cn/article/gID414wG.shtml

相关文章

Linux 内存管理 详解(虚拟内存、物理内存,进程地址空间)

Linux -操作系统内存管理 存储系统存储器的层次结构 Linux的内存管理物理内存物理内存管理 虚拟内存虚拟地址空间(写时拷贝) 和物理地址映射关系页表虚拟内存优缺点 「在 4GB 物理内存的机器上,申请 8G 内存会怎么样?」 计算机硬件的五大组成部分为&…

逆向汇编与反汇编——汇编基础快速入门

一、常用32位寄存器介绍 不同位数的寄存器的名称: eax:累加寄存器。通常用于算数运算,将结果保留在eax当中,当然也可以用于其他用途,比如一般把返回值通过eax传递出去。 ebx:基址寄存器 。有点类似于ebp…

汇编语言(四)——编程语法入门

目录 0.第一个汇编程序 1.语言常量 (1)整数常量 (2)实数常量 (3)字符常量 2.保留字 3.标识符 4.伪指令 5.指令 (1)标号 (2) 指令助记符 (3&#…

汇编语言轻松入门

​通常说的学习编程其实就是学习高级语言编程,比如C语言、C语言、Python语言、JAVA语言等等,即那些为人类设计的计算机语言。 但是,我们的计算机它并不理解什么是高级语言,计算机只是一个机器,它只有电气的特性&#…

汇编入门(一)

汇编入门(一) 1.第一个汇编程序 目前利用的是keil软件,建立文件时扩展名一定要为asm 如何查看具体地址存储器中的值: 在工具栏上选择“VIEW”>“memory windows”>“memory1” 然后输入地址即可: 注意这里…

汇编语言入门--调试工具debug的使用(史上最全,11种常见命令)

汇编语言入门–调试工具debug的使用(史上最全,11种常见命令) 1.直接启动debug程序 详见:https://bingshuai.blog.csdn.net/article/details/119978461 2.检查和修改寄存器内容命令R 检查 -r 修改 这里用AX来举例: -…

汇编语言入门(二)

汇编语言入门(一) 文章目录 寄存器【内存访问】内存中字的存储DS和【address】字的传送mov、add、sub指令数据段栈栈顶越界的问题PUSH和POP指令栈的综述栈段【栈的综述】 寄存器【内存访问】 内存中字的存储 4E20是两个字节构成一个字。 上图中0地址单…

汇编语言学习入门+亲自实操+图形并茂

汇编器 汇编器是将汇编语言转化为机器码的程序。或许你会以为汇编转化到机器码没什么大不了的,毕竟几乎是一对一的转换。但nasm存在的意义在于它可以很好的适应多种处理器平台,让编写汇编这件事都变得可移植了。nasm可以在Ubuntu下汇编,使用…

ARM 汇编语言入门

[翻译]二进制漏洞利用(二)ARM32位汇编下的TCP Bind shell:https://bbs.pediy.com/thread-253511.htm ARM汇编语言入门 From:ARM汇编语言入门(一):https://zhuanlan.zhihu.com/p/109057983 原文地址:https://azeria-labs.com/writi…

基础汇编语言编程

目录 什么是汇编语言? 工程搭建 新建工程 环境设置 测试是否成功 正式学习汇编语言 数据处理指令 填充,加,减,乘 思考:我们可以看到R0寄存器可以存放8位十六进制数,那么0x12345678能不能用mov存入&am…

汇编入门学习

学自狂神:最通俗易懂的计算机底层教学,二进制到汇编学习! 1、概述 学习路线: 语言 进制 进制如何运算 二进制 数据宽度 有符号数和无符号数 原码反码补码 位运算 位运算计算 汇编 寄存器 内存 汇编指令 内存复制 …

【学习笔记】汇编语言入门

汇编语言入门 概述 指令格式(最多包含4个区段):各区段之间用规定的分隔符分开 [标号:] 操作码 [操作数] [;注释]标号区段(当前指令行的符号地址)操作码区段(指令的操作行为,由操作…

关于汇编语言入门的几个案例

哈喽,大家好呀~这篇呢我们来看看汇编语言的几个入门小案例。 🥇个人主页:个人主页 🥈系列专栏:日常解决的问题 🥉与这篇汇编相关的文章: …

什么是汇编语言

汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地址符号&#…

汇编语言系列教程之基础入门 (一)

汇编语言系列教程之基础入门 (一) http://www.cnblogs.com/flyor/p/7208948.html 机器字长 机器字长是指CPU一次运算所能处理的数据的位数,一般来说这个数的和CPU的通用寄存器长度、数据总线的宽度等相等,在8086中为16bit。由于历…

快速入门汇编语言

这篇是我在先后学习了《汇编原理》、《CSAPP》第三章和《x86 data sheet》,以及经历了大量google后写出的总结性文档,用于自查和复习。若能有所助益,不胜荣幸。如有错漏,烦请不吝赐教。 1. 从C到汇编 在初学C语言时,…

汇编语言(一)

一、编程语言 1)机器语言 机器语言是机器指令的集合。在计算机中是一串0、1二进制数,计算机转变为高低电平,器件受到震动,进行运算。由于硬件设计和内部结构的不同,需要不同的电平脉冲来控制使它工作,所以…

汇编语言程序设计入门

前言:公号 「编程有料」后台回复 “大礼包” 即可获取近1000本助力你编程之路的电子书 汇编语言程序设计 一,汇编语言程序设计概述1,程序设计语言2,汇编语言源程序3,汇编语言程序开发过程 二,汇编语言基本语…

手把手教你学习汇编语言——从入门到起飞

专接本交流群:1051749714 (有什么问题欢迎进群讨论) 关于这篇博客,笔者会尽量用最简单的方式教会大家汇编语言以便应付各种考试 有什么不足的忘大佬评论或私信指出。 汇编环境安装 汇编运行调试 汇编指令 希望大家看完这些找一些…

小白的入门之——汇编语言程序设计教程

目录 第一章 基础知识 1.1汇编语言的特点 1.2计算机中数据表示的特点 【BCD码表示法(Binary Coded Decimal)】 【定点数】 —原码表示法— —反码表示法— —补码表示法— 浮点数 1.3 ​​​​​​计算机的数据存储 寄存器 存储器 数据在主…