内存管理-高端内存(下)

linux内核高端内存管理之固定内存区与映射 前面总结了高端内存中永久内核映射和临时内核映射。linux高端内存中的临时内存区为固定内存区的一部分,下面是Linux内存布局图 对于固定内存在linux内核中有下面描述 enum fixed_addresses { #ifdef CONFIG_X86_32 FIX_HOLE, FIX_VDSO, #else VSYSCALL_LAST_PAGE, VSYSCALL_FIR 阅读详情

固定映射的线性区从FIXADDR_START~FIXADDR_TOP,而临时内核映射区只是固定映射的线性区的一部分。固定映射用fixed_addresses中的索引从0xfffff000开始倒着往前分配固定地址的映射区。而临时内核映射其实就是永久映射的原子实现版本,它使用固定映射中FIX_KMAP_BEGIN到FIX_KMAP_END(它们都是的fixed_addresses中的枚举类型)这段区间。为了把一个物理地址与固定映射的线性地址关联起来,内核使用set_fixmap(idx, phys)和set_fixmap_nocache(idx, phys)宏。这两个函数都把fix_to_virt(idx)线性地址对应的一个页表项初始化为物理地址phys。 

[cpp]  view plain copy
  1. enum fixed_addresses {    
  2.     FIX_HOLE,    
  3.     FIX_VSYSCALL,    
  4. #ifdef CONFIG_X86_LOCAL_APIC    
  5.     FIX_APIC_BASE,  /* local (CPU) APIC) -- required for SMP or not */    
  6. #endif    
  7. #ifdef CONFIG_X86_IO_APIC    
  8.     FIX_IO_APIC_BASE_0,    
  9.     FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1,    
  10. #endif    
  11. #ifdef CONFIG_X86_VISWS_APIC    
  12.     FIX_CO_CPU, /* Cobalt timer */    
  13.     FIX_CO_APIC,    /* Cobalt APIC Redirection Table */     
  14.     FIX_LI_PCIA,    /* Lithium PCI Bridge A */    
  15.     FIX_LI_PCIB,    /* Lithium PCI Bridge B */    
  16. #endif    
  17. #ifdef CONFIG_X86_F00F_BUG    
  18.     FIX_F00F_IDT,   /* Virtual mapping for IDT */    
  19. #endif    
  20. #ifdef CONFIG_X86_CYCLONE_TIMER    
  21.     FIX_CYCLONE_TIMER, /*cyclone timer register*/    
  22. #endif     
  23. #ifdef CONFIG_HIGHMEM    
  24.     FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */    
  25.     FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,    
  26. #endif    
  27. #ifdef CONFIG_ACPI_BOOT    
  28.     FIX_ACPI_BEGIN,    
  29.     FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,    
  30. #endif    
  31. #ifdef CONFIG_PCI_MMCONFIG    
  32.     FIX_PCIE_MCFG,    
  33. #endif    
  34.     __end_of_permanent_fixed_addresses,    
  35.     /* temporary boot-time mappings, used before ioremap() is functional */    
  36. #define NR_FIX_BTMAPS   16    
  37.     FIX_BTMAP_END = __end_of_permanent_fixed_addresses,    
  38.     FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS - 1,    
  39.     FIX_WP_TEST,    
  40.     __end_of_fixed_addresses    
  41. };   
这里涉及到几个宏:
/*固定映射线性区的结束地址,距4G只有4KB*/

[cpp]  view plain copy
  1. #define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP)    
/*固定映射线性区的大小*/
[cpp]  view plain copy
  1. #define __FIXADDR_SIZE  (__end_of_permanent_fixed_addresses << PAGE_SHIFT)    
/*固定映射的线性区起始地址*/
[cpp]  view plain copy
  1. #define FIXADDR_START       (FIXADDR_TOP - __FIXADDR_SIZE)    
/*计算给定索引对应的线性地址*/
[cpp]  view plain copy
  1. #define __fix_to_virt(x)    (FIXADDR_TOP - ((x) << PAGE_SHIFT))    
/*计算线性地址对应的索引*/
[cpp]  view plain copy
  1. #define __virt_to_fix(x)    ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)    

所以,每个索引对应的线性地址是不变的,但是可以通过set_fixmap和set_fixmap_nocache映射到不同的物理地址。

临时内核映射的枚举结构:

[cpp]  view plain copy
  1. enum km_type {    
  2. D(0)    KM_BOUNCE_READ,    
  3. D(1)    KM_SKB_SUNRPC_DATA,    
  4. D(2)    KM_SKB_DATA_SOFTIRQ,    
  5. D(3)    KM_USER0,    
  6. D(4)    KM_USER1,    
  7. D(5)    KM_BIO_SRC_IRQ,    
  8. D(6)    KM_BIO_DST_IRQ,    
  9. D(7)    KM_PTE0,    
  10. D(8)    KM_PTE1,    
  11. D(9)    KM_IRQ0,    
  12. D(10)   KM_IRQ1,    
  13. D(11)   KM_SOFTIRQ0,    
  14. D(12)   KM_SOFTIRQ1,    
  15. D(13)   KM_TYPE_NR    
  16. };    
这里每个type是一个“窗口”,每个CPU都有它自己的包含13个窗口的集合,他们用enum km_type数据结构表示,该数据结构中定义的每个符号,如KM_BOUNCE_READ,KM_USER0或KM_PTE0,标识了窗口的线性地址。在高端内存的任何一个页框都可以通过一个“窗口”映射到内核地址空间。也就是说一个窗口对应一个4KB的物理页。


        内核必须确保同一个窗口永不会被两个不同的控制路径同时使用,也就是说后边的控制路径可以覆盖前边的。km_type结构中每个符号只能由一种内核成分使用,并以该成分命名。最后一个符号KM_TYPE_NR本身并不表示一个线性地址,但由每个CPU用来产生不同的可用窗口数。在km_type中每个符号都是固定映射的线性地址的一个下标。enum fixed_addresses数据结构包含符号FIX_KMAP_BEGIN和FIX_KMAP_END,把后者赋给下标FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1。在这种情况下,系统中每个CPU都有KM_TYPE_NR个固定映射的线性地址。

建立临时内核映射调用kmap_atomic:

[cpp]  view plain copy
  1. void *kmap_atomic(struct page *page, enum km_type type)    
  2. {    
  3.     enum fixed_addresses idx;    
  4.     unsigned long vaddr;    
  5.     
  6.     /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */    
  7.     inc_preempt_count();    
  8.     if (!PageHighMem(page))    
  9.         return page_address(page);    
  10.     
  11.     idx = type + KM_TYPE_NR*smp_processor_id();    
  12.     vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);    
  13. #ifdef CONFIG_DEBUG_HIGHMEM    
  14.     if (!pte_none(*(kmap_pte-idx)))    
  15.         BUG();    
  16. #endif    
  17.     set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));    
  18.     __flush_tlb_one(vaddr);    
  19.     
  20.     return (void*) vaddr;    
  21. }    

       这里先判断是否是高端内存,如果不是就直接返回page对应的线性地址。否则,通过type和CPU标识符smp_processor_id()来确定在固定映射地址中的索引值。获得这个索引值对应的线性地址,设置相应的页表项,然后返回线性地址。这里会让人产生思考的地方是,为什么是kmap_pte-idx而不是kmap_pte+idx呢?先来看一下kmap_pte的初始化在内核启动的时候:

[cpp]  view plain copy
  1. void __init kmap_init(void)      
  2. {      
  3.     unsigned long kmap_vstart;      
  4.     /* cache the first kmap pte */      
  5.     kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);      
  6.     kmap_pte = kmap_get_fixmap_pte(kmap_vstart);      
  7.       
  8.     kmap_prot = PAGE_KERNEL;      
  9. }      
  10. #define kmap_get_fixmap_pte(vaddr)                  \      
  11.     pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), vaddr), (vaddr)), (vaddr))   

   通过对照上边的宏可以看出来,kmap_pte是FIX_KMAP_BEGIN对应的线性地址所在的页表的线性地址。由于使用的是__fix_to_virt宏,所以kmap_pte应该是接近FIXADDR_TOP而不是接近FIXADDR_START的。也就是说fixed_addresses与km_type中索引大的接近FIXADDR_START,索引小的接近FIXADDR_TOP。所以set_pte的时候是kmap_pte- idx。
撤销临时内核映射调用kunmap_atomic:

[cpp]  view plain copy
  1. void kunmap_atomic(void *kvaddr, enum km_type type)    
  2. {    
  3. #ifdef CONFIG_DEBUG_HIGHMEM    
  4.     unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;    
  5.     enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();    
  6.     
  7.     if (vaddr < FIXADDR_START) { // FIXME    
  8.         dec_preempt_count();    
  9.         preempt_check_resched();    
  10.         return;    
  11.     }    
  12.     
  13.     if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx))    
  14.         BUG();    
  15.     
  16.     /*  
  17.      * force other mappings to Oops if they'll try to access  
  18.      * this pte without first remap it  
  19.      */    
  20.     pte_clear(kmap_pte-idx);    
  21.     __flush_tlb_one(vaddr);    
  22. #endif    
  23.     
  24.     dec_preempt_count();    
  25.     preempt_check_resched();    
  26. }  <span style="line-height: 26px; "> </span>  

撤销的时候清除了相应的页表项。    

    综上,kernel中的高端内存已经研究完了。总结一下:高端内存的引入是为了kernel可以访问大于1G的物理内存(不是同一时刻),划出一个128MB的窗口来自由映射大于1G的内存。vmalloc()主要是建立动态分配和释放的内存区,但是建立和释放的过程非常复杂,需要对pgd,pud,pmd,pte进行修改。这里是修改master kernel page global directory,进程的内核页部分需要在访问时产生缺页异常然后再同步。而永久内核映射就简单的多,如果没有开PAE,则有4MB的线性地址可以用来映射,4MB当然是只有一个页表就够用了,这个专门的页表地址存放在pkmap_page_table变量中。只需要设置这个页表中相应的表项就可以了,一共1024个表项,每个对应一个4KB的页,因为页比较少,如果页耗尽的时候会导致进程阻塞,这样就不能用在中断处理程序中。而临时内核映射则更加简单了,其实就是永久内核映射的原子实现版,它利用固定内核映射中的一段空间,为每个CPU保存13个窗口,每个窗口的功能是固定的,不同进程需要分配同一个窗口的时候就进行覆盖,所以不会导致进程阻塞,可以用于中断处理程序和可延迟函数的内部。

如何在iPhone上本地运行SmolVLM?5亿参数模型实战指南(附Swift代码) 本文详细介绍了如何在iPhone上本地部署和运行轻量高效的SmolVLM多模态模型。通过实战指南,分享了使用Swift和MLX框架进行环境配置、模型加载、内存优化以及实现实时图像分析与文本生成的完整流程,帮助开发者在移动端高效运行AI视觉语言模型。 阅读详情

相关推荐

解决UE5像素流三大痛点:鼠标锁定/双光标/自动播放(附app.js魔改指南)

本文针对UE5像素流服务在实际应用中常见的鼠标锁定、双光标和自动播放三大交互痛点,提供了深入的技术分析和解决方案。通过详细解析app.js源码,指导开发者如何修改控制方案、修复光标显示以及实现静音自动播放,旨在提升Pixel Streaming应用的用户体验与专业度。

weixin_28025327的博客 381

Linux内存 -- Fixmap映射固定虚拟地址

继上文介绍内核在启动初期为内核段建立了临时映射页表,实现了内核物理地址到虚拟地址的映射;该映射完成后可以通过虚拟地址访问内核空间,但此段仅限于内核段的访问,内核之外的内存空间还不能使用虚拟地址进行访问,需要继续完成内存空间外的内存映射。要实现其他内存空间的映射就需要从fdt中解析系统的内存情况,要访问fdt就要先实现fdt的映射,即要引出本章的介绍的主角fixmap。

hello_yj的专栏 1180

IAR9.4报错之编码方式

IAR9.40编码方式错误导致的报错

qq_36081075的博客 2624

Linux kernel内存管理基础

vmalloc与ioremap在驱动中才能用,因为申请的虚拟内存是在内核的地址范围内,是从VMALLOC区域申请的。ioremap从VMALLOC区申请到虚拟地址后,直接映射(因为物理地址已知): virtual addr <–> regsiters address。vmalloc从VMALLOC区申请到虚拟地址后,需要申请size大小的物理地址空间(vmalloc参数指定),然后在映射: virtual addr <–> 物理地址。

Catdeo的博客 1848

浙政钉H5应用上架实战:从环境配置到发布避坑指南

本文详细解析浙政钉H5应用上架全流程,从环境配置到发布避坑指南。重点介绍Vue 2.6 + Node 14.21.3的稳定开发环境搭建、项目结构规范及常见报错解决方案,帮助开发者高效完成浙政钉H5应用上架流程。

weixin_27943395的博客 284

PID控制器(位置式和增量式)

PID控制算法

zhinian_hao的博客 4512

深入解读Linux内存管理系列(6)——地址空间划分

日期 内核版本 CPU架构 作者 2019.04.06 Linux-4.4 PowerPC LoneHugo 系列文章:https://blog.youkuaiyun.com/Vince_/article/details/89055979 虚拟地址空间划分,包括一致映射,临时映射,永久映射,vmalloc,ioremap区域等 内存布局 enum fixed_...

Hugo的博客 813

【计算机三级-数据库技术】操作题大题(第1套)

从触发器的语法中可知,【1】处应该填写的是执行触发器的表,【2】处应该填写指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。使用FOR或AFTER选顶定义的触发器为后触发器,即只有在引发触发器执行的语句中的操作都已成功执行,并且所有的约束检查也成功完成后,才执行触发器。转换成新的独立的模式,模式的属性由联系本身的属性及两个实体的键构成,主键由两端实体的键组合而成。实体的转换:在从ER图转换为关系模式时,一个实体转换成一个关系模式,实体的属性就是关系模式的属性,实体的键就是关系的主键。

放纵小开爱的博客 1101

Linux kernel内存分配基础

kernel内存分配基础

weixin_42136255的博客 1333

MongoDB分布式事务实现:两阶段提交、日志复制与冲突解决

MongoDB分布式事务机制解析 本文深入剖析了MongoDB 4.0+版本的分布式事务实现原理,重点分析了其在跨文档/跨集合/跨分片场景下的数据一致性解决方案。核心内容包括: 底层机制:采用改进的两阶段提交协议,通过oplog日志复制实现无中心协调器的事务管理,结合WiredTiger存储引擎和锁机制保障ACID特性。 创新设计:将传统2PC的中心协调功能分散到Primary节点,利用oplog作为事务状态记录,实现自动故障恢复和60秒超时回滚机制。 性能优化:针对大事务、写冲突等常见问题,提出拆分微事务

数据知道的博客 1261

解决群晖NAS无法拉取docker镜像问题

在2024-06-01日以前,使用SSH登录后是可以直接拉取docker镜像的;但是之后当我们使用SSH登录上群晖NAS,使用【sudo -i】切换到超管拉取镜像会提示报错【error pulling image configuration: Get "https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/cf/cff6b68a194a672fd337d8728bfb252071012cce6

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 2万+

Linux内存管理 | 三、虚拟地址空间管理

上一节,我们主要了解了虚拟内存空间的布局情况,趁热打铁,我们直接从源代码的视角,来看一下`Linux`内核是如何管理虚拟内存空间的。 废话不多说,直接开始!

Donge's Blog 889

华为OD机试真题 新系统 2026-04-26 Python&JS 实现【项目模块依赖构建顺序规划】

某公司正在开发一个大型软件系统,系统包含 N 个模块,每个模块之间存在构建依赖关系。例如,模块 A 可能依赖于模块 B,这意味着必须先构建模块 B,才能构建模块 A。 请根据依赖关系,输出所有可能的模块构建顺序(按照构建顺序排列模块名称),要求:

MISAYAONE的博客 403

Linux内存管理之高端内存

Linux对内存的管理划分成三个层次,分别是Node、Zone、Page。对这三个层次简介如下: 层次 说明 Node(存储节点) CPU被划分成多个节点,每个节点都有自己的一块内存,可以参考NUMA架构有关节点的介绍 Zone(管理区) 每一个Node(节点)中的内存被划分成多个管理区域(Zone),用于表示不同范围的内存 Page(页面) 每一个管理区又进...

ibless的博客 6685

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Linux 内存管理 —— 高端内存

高端内存

Jia ming 的日常学习笔记 1027

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Linux内核高端内存管理

Linux内核高端内存管理

qq_43678612的博客 129

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

内存管理-高端内存

进程可以寻址4G,其中0~3G为用户态,3G~4G为内核态。如果内存不超过1G那么最后这1G线性空间足够映射物理内存了,如果物理内存大于1G,为了使内核空间的1G线性地址可以访问到大于1G的物理内存,把物理内存分为两部分,0~896MB的进行直接内存映射,也就是说存在一个线性关系:virtual address = physical address + PAGE_OFFSET,这里的PAGE_

michaelcao1980的专栏 3592

linux内存管理之高端内存

linux内核只能访问1G的内存空间,对于超出1G的内存,需要借助于page管理 linux 用户空间与内核空间——高端内存详解 https://blog.youkuaiyun.com/Tommy_wxie/article/details/17122923...

shipinsky的博客 202

Linux内核-内存管理之高端内存

看了linux内核内存管理,参考网上的意见整理了一下,认为两个比较好的博文: http://blog.youkuaiyun.com/chobit_s/article/details/6029527 http://blog.youkuaiyun.com/do2jiang/article/details/5495675 1.页框管理        Linux采用4KB页框大小作为标准的内存分配单元。内

继续微笑lsj 2166

linux 内核高端内存意义,Linux内核高端内存管理

原先一直都对Linux高端内存的管理认识模模糊糊的,可能主要是初次接触Linux kernel 是0.11版吧,当初的内存设计是16M,Linus对拥有32M的内存都是觊觎万分,1G内存恐怕是天方夜谭了。16M内存哪里还顾得上高端内存,脑子就一直没有这种概念。先我们还是来看看为什么要有高端内存?80386的线性寻址空间是4G,内核空间从3G开始,如果全部采用"线性映射"(物理地址和逻辑地址只差一个...

weixin_26786277的博客 265

linux高端内存管理之非连续内存区(描述)

总结了高端内存区的固定内核映射区、临时内核映射与永久内核映射。但是对于高端内存中各个区间的布置我们任然不是很清楚,首先我们从整体上看看内核对高端内存的划分情况。 如果内存足够大(比如用户:内核线性空间=3:1,内核就只能访问线性空间的第4GB内容,如果物理内存超过1GB则视为足够大),内核线性空间无法同时映射所有内存。这就需要将内核线性空间分出一段不直接映射物理内存,而是作为窗口分时映射使用

bullbat的专栏 4474

Linux内存描述之高端内存--Linux内存管理(五)

原文链接:https://blog.youkuaiyun.com/gatieme/article/details/52384791 日期 内核版本 架构 作者 GitHub 优快云 2016-09-01 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 1 前景回顾 前面我们讲到服务器体系(SMP, NUMA, MPP)与共享存储器架构(UMA和NUMA) 1.1 UMA和

weixin_39094034的博客 551

Linux 内存管理 -- 高端内存的映射方式

http://blog.youkuaiyun.com/chenziwen/article/details/5932396   Linux 内存管理 -- 高端内存的映射方式 分类: server开发2010-10-11 00:27 1146人阅读 评论(1) 收藏 举报 linuxstructlinux内核数据结构table算法   高端内存是指物理地址大于 896M 的内存。

zdy0_2004的专栏 1308

内存管理——高端内存

作者:文松 链接:https://www.zhihu.com/question/280526042/answer/1615449221 来源:知乎 一、高端内存的由来(为什么需要高端内存) 在32位地址时代,最大可寻址0xFFFFFFFF,即4GB,因此虚拟地址空间有4GB,通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间,即Linux内核虚拟地址空间只有1G。 32位地址空间 实际的计算机体系结构有硬件的限制,这约束了页框的使用方式,其中,Linux内核必须处理x.

u014426028的博客 2050

linux高端物理内存,Linux内存管理之高端内存

看了Linux内核内存管理,参考网上的意见整理了一下。1.页框管理Linux采用4KB页框大小作为标准的内存分配单元。内核必须记录每个页框的状态,这种状态信息保存在一个类型为page的页描述符中,所有的页描述存放在mem_map中。virt_to_page(addr)产生线性地址对应的页描述符地址。pfn_to_page(pfn)产生对应页框号的页描述符地址。在页框描述符中,几个关键的字段我认为:...

weixin_32447969的博客 362

Linux内存管理:内存描述之高端内存

《Linux内存管理:内存描述之内存节点node》 《Linux内存管理:内存描述之内存区域zone》 《Linux内存管理:内存描述之内存页面page》 《Linux内存管理:内存描述之高端内存》 《Linux用户空间与内核空间(理解高端内存)》 目录 1 前景回顾 1.1 UMA和NUMA两种模型 1.2 Linux如何描述物理内存 1.3 典型架构(x86)上内存区域划分 2 内核空间和用户空间 2.1 把内核空间和用户空间分开 2.2 linux为什么把内核映射到3G-4G这

RToax 1136
上一篇: 浅析linux内核内存管理之高端内存(上)
下一篇: Request_irq和setup_irq的区别
白水煮鸡蛋
博客等级 码龄15年 106粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值