
内存管理
深入浅出的介绍Linux内核内存管理机制。
程序猿Ricky的日常干货
擅长扫地、打杂、开车。。
展开
-
NUMA架构对于程序设计的影响
NUMA指的是非一致性访问模型,现在越来越多的大型计算机统中采用了NUMA架构的设计,主要是它的扩展性好,也可以降低成本,同一个机位可以存放一个更多核心和更多内存的机器,自然可以降低IDC的建设成本。对于一个NUMA系统来说,CPU是属于不同的节点node的,内存也是属于不同的node,那么在访问内存时如果是相同node下的CPU和内存之间的访问,那么速度很快,而如果是跨越了node去访问内存,那么速度就会明显变慢。这就带来一个问题,很多应用开发者对于硬件架构的了解并不是很多,如果一个应用进程在申请内存时原创 2020-12-03 11:31:20 · 557 阅读 · 0 评论 -
如何理解内存管理中的alloc_flags?
首先需要明确一点alloc_flags和gfp_mask之间的区别,gfp_mask是使用alloc_pages申请内存时所传递的申请标记,而alloc_flags是在内存管理子系统内部使用的另一个标记,二者是不同的,当然alloc_flags也是从gfp_mask经过计算得到的。关于alloc_flags的定义有如下几个:/* The ALLOC_WMARK bits are used as an index to zone->watermark */#define ALLOC_WMARK_M原创 2020-11-12 17:14:43 · 1058 阅读 · 0 评论 -
内存申请标记注意区分___GFP_HIGH和___GFP_HIGHMEM
背景经常在看内存管理这块的代码时会看到这两个标记,有时候稍微不注意就会把他们搞混了,本篇博客就来聊聊他们的区别。定义首先这两个标记的定义如下,由此可见他们两个是不同的两个标记,表示的当然也是不同的意义:#define ___GFP_HIGH 0x20u#define ___GFP_HIGHMEM 0x02u对于___GFP_HIGHMEM 标记来说,它是用来在内存申请时确定要从哪个目标zone中申请内存的。具体可以参考我的博文:《如何从GFP确定最后申请的内存来自原创 2020-11-12 17:01:18 · 1694 阅读 · 0 评论 -
__alloc_pages_nodemask是如何申请内存的?
本文基于linux3.10Fast path:get_page_from_freelist – First allocation attemptusing low watermark:gfp_mask |= __GFP_HARDWALL;alloc_flags = ALLOC_WMARK_LOW|ALLOC_CPUSET|ALLOC_FAIR;第一次尝试申请空闲内存,使用LOW水位判断是否足够申请。get_page_from_freelist – retryusing low waterm原创 2020-11-12 16:39:39 · 989 阅读 · 0 评论 -
如何从GFP确定最后申请的内存来自哪个zone?
申请内存时需要先确定两个值,分别是high_zoneidx和migratetype。这两个值从哪里获取到呢?都是利用上面传递过来的GFP flag。对于high_zoneidx是通过gfp_zone函数获取的,而migratetype是通过gfpflags_to_migratetype来获取的。其中gfp_zone是利用的GFP标志位的bit[0-3]这4个bits来确定的,而migratetype是bit[3-4]这两个bits。需要注意他们中间是存在重复的__GFP_MOVABLE标志的,说明它不仅仅原创 2020-11-12 11:04:37 · 928 阅读 · 0 评论 -
记一个对MMU的错误理解
偶然和同学交流技术时,思想碰撞,引发了一个对于MMU和CPU架构的思考。按照我以往的理解,MMU就是一个内存管理单元,是负责虚拟地址到物理地址转换功能的。我一直认为一个SOC中只需要一个MMU做这个工作就行了,事实证明我还是想当然了。。。我们知道对于MMU来说,它的工作需要页表来进行支持,一般内核需要配置相关寄存器,(页表基地址寄存器),在内核中每个进程都拥有自己的用户地址空间,在task_struct中都会保存一个pgd成员,它的作用就是在进程切换时对地址空间进行切换,实际上也就是把这个值告诉MMU。原创 2020-09-16 15:49:03 · 849 阅读 · 0 评论 -
page fault的两种区别(major、minor)
page fault缺页异常分为两种类型,一种叫做major page fault,这种类型的缺页可以通过 Disk IO来满足,另一种叫做minor page fault,这种缺页可以直接利用内存中的缓存页满足。区别对于IO子系统来说,内核中的分层结构从上到下:VFS--> EXT4/EXT3-->Page Cache--> General Block Layer --> IO Scheduler --> Disk Driver那么如果访问一个地址时,与该地址空间v原创 2020-09-16 15:29:07 · 10232 阅读 · 2 评论 -
AARCH64 启动过程中的内存映射与管理(基于kernel-4.9)
1.汇编阶段会进行identity map和kernel image mapidentity map:是指把idmap_text区域的物理地址映射到相等的虚拟地址上,这种映射完成后,其虚拟地址等于物理地址。idmap_text区域都是一些打开MMU相关的代码,这中映射就保证了打开MMU的前后,代码的执行寻址不会出错,从而可以无缝切换的。kernel image map:这个映射操作是将KE...原创 2018-04-16 11:39:54 · 2213 阅读 · 0 评论 -
page cache回写的几种触发方式
1.通过写入proc文件系统节点操作echo 3 > /proc/sys/vm/drop_cachesdrop_caches是会把所有page cache都释放或者写回的。2.sysctl设置系统参数配置flushd进程把脏页写回到磁盘的是时间间隔,单位是1/100秒,因此如下配置为5s的间隔:vm.dirty_writeback_centisecs = 500配置内存中脏页...原创 2020-04-27 09:35:12 · 1751 阅读 · 0 评论 -
Linux内核如何区分匿名页映射?
page结构体关键成员内核中使用page结构体来描述物理内存,每个物理页就对应一个page结构体来描述,所以page结构体占用的内存大小是与系统物理内存大小成正比的。物理内存越大,用于描述物理页的page结构体就越多,占用的内存也就越大,因此为了减少内存的占用,page结构体大量使用了union联合体结构。如下我们只列举了本文下面要讲述的一些page成员。include/linux/mm_ty...原创 2019-07-29 11:44:30 · 1778 阅读 · 0 评论 -
深入浅出内存管理--页分配器
页分配器页分配器是用于整页内存申请的,伙伴系统也是针对页为基本单位的一种算法,因此我们的页分配器是会使用伙伴系统算法的上层接口,为了优化伙伴系统的效率,内核还实现了一个per-cpu的页框高速缓存,这种高速缓存是为了提升页分配器的效率而存在的,当我们申请单个页框的时候会优先从高速缓存中申请,而不用每次都运行伙伴系统算法来进行寻找,这是对伙伴系统的一种补充和优化。下面我们将主要介绍页分配器的上层接...原创 2018-12-23 17:08:19 · 492 阅读 · 0 评论 -
Linux cma内存的使用
CMA的全称叫做contiguous memory allocator,它是为了便于进行连续物理内存申请的一块区域,一般我们把这块区域定义为reserved-memory。早期的Linux内核中没有cma的实现,如果驱动想要申请一个大块的物理连续内存,那么只能通过预留专属内存的形式,然后在驱动中使用ioremap来映射后作为私有内存使用。这样带来的后果就是有一部分内存将被预留出来不能作为系统中的...原创 2020-05-06 14:54:22 · 7264 阅读 · 0 评论 -
对于MIGRATE_MOVABLE的理解
内存管理对于不同的zone管理区,都会划分为不同的memory block进行管理,这些memory block是按照2的幂次个页面进行管理的,对应的结构体如下:struct zone { ... /* free areas of different sizes */ struct free_area free_area[MAX_ORDER]; ...}...原创 2020-04-30 15:45:01 · 1671 阅读 · 0 评论 -
对于ZONE_MOVABLE的理解
内核中的管理区内核中定义了如下一些管理区zone:enum zone_type {#ifdef CONFIG_ZONE_DMA /* * ZONE_DMA is used when there are devices that are not able * to do DMA to all of addressable memory (ZONE_NORMAL). ...原创 2019-01-15 19:57:41 · 6427 阅读 · 0 评论 -
ARM 64架构内存布局
首先我们以最为常用的4KB page + 3 levels配置为例去介绍: AArch64 Linux memory layout with 4KB pages + 3 levels: Start End Size Use -------------------------------------------------------...原创 2019-01-15 17:25:56 · 14291 阅读 · 0 评论 -
内存管理--KSM
KSM (Kernel Samepage Merging)内核合并页,目的是为了把内容完全相同的页面合并,从而释放内存供其他应用使用。Linux对虚拟机的支持,增加了相同页面的数量,所以KSM存在的合理性也就越来越大了,KSM页面在struct page中的page->mapping中标识,并且KSM页面一定也是一个匿名页。#define PAGE_MAPPING_ANON 1#...原创 2019-01-10 14:40:28 · 1679 阅读 · 0 评论 -
内存管理--KASAN
KASAN是用于内存debug使用的一种机制,通过内核中使能该选项: CONFIG_KASAN = y它的原理就是,通过内存中分配一块专用内存为“影子内存”(shadow memory)用来标记系统可以用内存的状态,类似于内核中使用struct page结构体,只不过影子内存比struct page更加浪费内存,它与可用内存的比例为1:8。因为影子内存是把内存按照8 Byte来标记,标志位如...原创 2019-01-09 16:13:44 · 3691 阅读 · 5 评论 -
slab着色
对于大小相同的slab object,虽然处于不同的物理地址处,但是由于cache访问地址的规则,这两个地址很有可能会被分配给同一个cache line去加载,那么会带来一个问题,比如软件中反复去读取这样的两个对象,那么会带来什么后果呢?我们发现对应的cache line需要被反复刷新,而其余的cache line却未被充分利用。我们知道不同的cache line只能加载特定地址偏移的地址,比...原创 2019-01-09 15:03:49 · 718 阅读 · 0 评论 -
内存回收
简介内存回收是内存管理最为复杂的机制,本文主要在广义上介绍回收方法,而不拘泥于细节。随着系统的运行,内存会逐渐被消耗,而内存主要占用的部分有两个,分别是高速缓存以及用户进程,内核高速缓存包括磁盘高速缓存,slab缓存等。首先介绍内核中内存回收的几种方式:直接内存回收当我们调用内存分配函数,比如alloc_pages时,发现此时对应zone管理区中的watermark低于min值,就会触发...原创 2019-01-09 14:36:53 · 5416 阅读 · 0 评论 -
深入浅出内存管理--kmalloc支持的最大内存分配
首先我们来看下kmalloc的实现,本文基于kernel 4.0版本:static __always_inline void *kmalloc(size_t size, gfp_t flags){ if (__builtin_constant_p(size)) { -----------(1) if (size > KMALLOC_MAX_CACHE_SIZE)...原创 2019-01-04 14:34:59 · 8855 阅读 · 0 评论 -
深入浅出内存管理--slab分配器
存在的意义页分配器只能按页为单位进行内存分配,但对于不足一页的申请,如果依然按一页来分配,就会造成内存的浪费,slab分配器就是为了完成小内存的分配和管理的。slab分配器建立在页分配器之上,它最终也是从页分配器申请得到整页内存,但是它对页内内存进行了更细化的管理,这一套机制有助于缓解内碎片问题。设计思想slab分配器把内存区看作对象来进行管理,并对外提供申请和释放的接口。它的核心是维护一个...原创 2018-12-31 22:13:00 · 757 阅读 · 0 评论 -
深入浅出内存管理--alloc_pages与free_page分析(框图)
alloc_pages从前面文章的介绍来看,alloc_pages用于连续物理内存的分配,它的实现如下图所示:从这个流程图来分析,函数是一步一步调用到buffered_rmqueue的,alloc_pages是页分配器的对外接口,系统中很多模块和驱动可以直接调用它申请到内存,而buffered_rmqueue是底层伙伴系统算法的实现。它的流程图如下所示:这里框图介绍了buffered_rm...原创 2018-12-30 20:54:22 · 4524 阅读 · 0 评论 -
深入浅出内存管理--高端内存映射之fixmap(固定映射)
我们知道系统中的高端内存是不能作为直接映射区映射到内核空间的,那么我们想要使用它怎么办呢?前面的文章我们已经有过相关的介绍,可以使用三种方法,分别是pkmap(永久映射)/fixmap(临时固定映射)/vmlloc,本文主要介绍fixmap,也就是固定映射又叫临时映射。关键点通过前面的介绍我们知道,fixmap和pkmap的最大区别是fixmap不会被阻塞,因此可以在中断上下文中使用。那会不会...原创 2018-12-23 16:32:23 · 2258 阅读 · 6 评论 -
深入浅出内存管理--高端内存映射之pkmap(永久映射)
我们知道系统中的高端内存是不能作为直接映射区映射到内核空间的,那么我们想要使用它怎么办呢?前面的文章我们已经有过相关的介绍,可以使用三种方法,分别是pkmap(永久映射)/fixmap(临时固定映射)/vmlloc,本文主要介绍pkmap,也就是永久映射。入口函数首先我们来介绍pkmap的入口函数,它就是kmap:void *kmap(struct page *page){ mig...原创 2018-12-23 16:00:01 · 2748 阅读 · 0 评论 -
深入浅出内存管理-- 伙伴系统(buddy system)
buddy system伙伴系统是内核中用来管理物理内存的一种算法,我们知道内存中有一些是被内核代码占用,还有一些是被特殊用途所保留,那么剩余的空闲内存都会交给内核内存管理系统来进行统一管理和分配,内核中会把内存按照页来组织分配,随着进程的对内存的申请和释放,系统的内存会不断的区域碎片化,到最后会发现,明明系统还有很多空闲内存,却无法分配出一块连续的内存,这对于系统来说并不是好事。而伙伴系统...原创 2018-12-17 18:57:39 · 20028 阅读 · 0 评论 -
深入浅出内存管理--对于lowmem_reserve的理解
每个内存管理区(zone)都有一个lowmem_reserve字段,它代表本管理区预留的物理内存大小。所谓预留,那就是说肯定还是有用的,只是用的时机不同。之前的文章中由介绍过water mark水位值,系统中已经为每个管理区(zone)内存大小设定了三个水位值了,分别时WMARK_HIGH/WMARK_LOW/WMARK_MIN。怎么这里又多出来一个lowmem_reserve?在内存分配时,...原创 2018-12-16 15:12:35 · 4202 阅读 · 0 评论 -
深入浅出内存管理--对于watermark的理解
经过前面文章的介绍,每个内存管理区都有一个数组watermark,内核中定义了三个watermark来表示当前系统剩余的空闲内存。WMARK_MIN,WMARK_LOW,WMARK_HIGH,watermarkhigh当剩余内存在high以上时,系统认为当前内存使用压力不大。low当剩余内存降低到low时,系统就认为内存已经不足了,会触发kswapd内核线程进行内存回收处理m...原创 2018-12-16 15:10:06 · 5275 阅读 · 0 评论 -
深入浅出内存管理--页描述符(Page)
内核中采用struct page来描述物理内存页,它的主要成员如下(非全部成员):unsigned long flags;标志位,内核中每个page的状态可以由此标志位来表示,列举几个标志位:PG_locked 页被锁定,比如在磁盘I/O操作中涉及到的页,不能被页框回收算法回收。PG_reserved 页留给内核代码或者没有使用,这种页是不受内存管理系统用于分配的,所以不能被回收算法回...原创 2018-12-16 14:13:56 · 1538 阅读 · 0 评论 -
深入浅出内存管理--内存管理区(Zone)
本文以Linux内核4.9来做介绍。内存管理区(ZONE)结构体每个Node节点中的内存又划分为多个ZONE来进行管理,内核中一共定义有如下几种类型的ZONE。enum zone_type {#ifdef CONFIG_ZONE_DMA /* * ZONE_DMA is used when there are devices that are not able ...原创 2018-12-14 17:50:17 · 2309 阅读 · 0 评论 -
深入浅出内存管理--内存节点(Node)
本文以Linux内核4.9来做介绍。Node 结构体内核中的节点是使用一个结构体struct pglist_data来进行管理的,它的组成如下所示,本文只会列出几个关键成员,其余成员待遇到时在做解释: typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; struct zonelist nod...原创 2018-12-14 15:36:09 · 3351 阅读 · 0 评论 -
深入浅出内存管理--物理内存框架
NUMA首先需要介绍一个NUMA的概念,非一致性内存访问模型,在这种系统中,CPU访问不同内存单元的时间可能是不一样的,物理内存被划分为不同的Node节点来进行管理,对于单CPU的系统也可能使用NUMA,因为这些系统的物理内存有可能不一定是整块的,而是中间包含有很大的洞,因此划分出多个Node进行管理。比如系统中存在两个内存芯片并且内存芯片的地址不连续,内核对应的也把物理内存划分为多个内存Nod...原创 2018-12-14 14:07:03 · 533 阅读 · 5 评论 -
深入浅出内存管理--高速缓存(cache memory)和转换后援缓冲(TLB)
高速缓存(Cache memory)CPU的运行速度时非常快的,当今的CPU主频都是GHZ级别的,而对于内存DDR来说,每次存取操作都会耗用很多的时钟周期,这意味着,CPU需要等待很长时间来完成一次读或者写操作。为了缩小CPU和DDR两者之间速度上的不匹配造成的等待问题,硬件上引入高速缓存,这利用了局部行原理,简单来说,该原理的想表达的就是:CPU运行所需要的内存一般都在当前访问地址的附近,最...原创 2018-12-16 22:25:13 · 2563 阅读 · 0 评论 -
深入浅出内存管理--内存管理概述(arm32内存布局)
内存管理我的理解是分为两个部分,一个是物理内存的管理,另一个部分是物理内存地址到虚拟地址的转换。物理内存管理内核中实现了很多机制和算法来进行物理内存的管理,比如大名鼎鼎的伙伴系统,以及slab分配器等等。我们知道随着Linux系统的运行,内存是不断的趋于碎片化的,内存碎片分为两种类型,一种为外碎片,所谓外碎片就是以页为单位的内存之间的碎片化,另一种为内碎片,内碎片是指同一个页面内的碎片化,那么...原创 2018-12-12 17:26:07 · 10753 阅读 · 0 评论 -
深入浅出内存管理--页表的创建
页表的创建Linux在启动过程中,要首先进行内存的初始化,那么就一定要首先创建页表。我们知道每个进程都拥有各自的进程空间,而每个进程空间又分为内核空间和用户空间。以arm32为例,每个进程有4G的虚拟空间,其中0-3G属于用户地址空间,3G-4G属于内核地址空间,内核地址空间是所有进程共享的,因此内核地址空间的页表也是所有进程共享的。Linux内核中内存的管理是通过一个结构体mm_struc...原创 2018-12-03 20:05:08 · 7160 阅读 · 0 评论 -
深入浅出内存管理-虚拟地址和物理地址转换
谈起内存管理,首先我们就要搞清楚虚拟地址和物理地址的关系。本文就是简单介绍下这两个基础概念。物理地址物理地址指实际存在的物理内存地址,比我有一个2G的内存芯片,那么系统的物理内存就是2G,我要访问该内存中的一个地址,那就需要对应的物理内存。虚拟地址虚拟地址,就是就是一种逻辑意义上的地址,而当我们想要访问这个虚拟地址时,是需要转换到物理地址才能够真实的访问到,比如我一个2G的内存,那么虚拟地...原创 2018-12-01 11:24:46 · 8432 阅读 · 1 评论 -
深入浅出内存管理-Linux内核页表
内核页表实现新版本的Linux内核代码中支持4级映射,那么一个虚拟地址是包含有如下几个部分:PGD:Page Global Directory,L0级别页表PUD:Page Upper Directory,L1级别页表PMD : Page Middle Directory,L2级别页表PTE : Page Table Entry,L3级别页表PageOffset:in-pag...原创 2018-11-29 21:04:52 · 1487 阅读 · 0 评论