1 固定映射

1.1 数据结构

linux高端内存中的临时内存区为固定内存区的一部分, 对于固定内存在linux内核中有下面描述

x86 arm arm64
arch/x86/include/asm/fixmap.h?v=4.7, line 67 arch/arm/include/asm/fixmap.h?v=4.7, line 11 arch/arm64/include/asm/fixmap.h?v=4.7, line 36
/*
* Here we define all the compile-time 'special' virtual
* addresses. The point is to have a constant address at
* compile time, but to set the physical address only
* in the boot process.
*
* These 'compile-time allocated' memory buffers are
* page-sized. Use set_fixmap(idx,phys) to associate
* physical memory with fixmap indices.
*
*/
enum fixed_addresses {
FIX_HOLE, /*
* Reserve a virtual window for the FDT that is 2 MB larger than the
* maximum supported size, and put it at the top of the fixmap region.
* The additional space ensures that any FDT that does not exceed
* MAX_FDT_SIZE can be mapped regardless of whether it crosses any
* 2 MB alignment boundaries.
*
* Keep this at the top so it remains 2 MB aligned.
*/
#define FIX_FDT_SIZE (MAX_FDT_SIZE + SZ_2M)
FIX_FDT_END,
FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1, FIX_EARLYCON_MEM_BASE,
FIX_TEXT_POKE0,
__end_of_permanent_fixed_addresses, /*
* Temporary boot-time mappings, used by early_ioremap(),
* before ioremap() is functional.
*/
#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE)
#define FIX_BTMAPS_SLOTS 7
#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS) FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1, /*
* Used for kernel page table creation, so unmapped memory may be used
* for tables.
*/
FIX_PTE,
FIX_PMD,
FIX_PUD,
FIX_PGD, __end_of_fixed_addresses
};

1.2 固定映射

ioremap的作用是将IOBIOS以及物理地址空间映射到在896M至1G的128M的地址空间内, 使得kernel能够访问该空间并进行相应的读写操作。

start_kernel()->setup_arch()->early_ioremap_init()

然后arm和arm64上early_ioremap_init又是early_ioremap_setup的前端

函数 x86 arm arm64
early_ioremap_init arch/x86/mm/ioremap.c?v=4.7, line 445 arch/arm/mm/ioremap.c?v=4.7, line 489 arch/arm64/mm/ioremap.c?v=4.7, line 110
early_ioremap_setup mm/early_ioremap.c?v=4.7, line 67 体系结构无关 体系结构无关
/*
* Must be called after early_fixmap_init
*/
void __init early_ioremap_init(void)
{
early_ioremap_setup();
}

但是arm和arm64下的setup_arch函数则会先调用early_fixmap_init函数来填充fixmap. 参见arch/arm/kernel/setup.c?v=4.7, line 1058arch/arm64/kernel/setup.c?v=4.7, line 229.

void __init setup_arch(char **cmdline_p)
{
early_fixmap_init();
early_ioremap_init();
}

early_fixmap_init函数的定义在

arm arm64
arch/arm/mm/mmu.c?v=4.7, line 385 arch/arm64/mm/mmu.c?v=4.7, line 676

其中arm架构的定义如下所示, 在arch/arm/mm/mmu.c?v=4.7, line 385

void __init early_fixmap_init(void)
{
pmd_t *pmd; /*
* The early fixmap range spans multiple pmds, for which
* we are not prepared:
*/
BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)
!= FIXADDR_TOP >> PMD_SHIFT); /*得到固定映射区的pmd
,此pmd为虚拟地址转换为物理地址的pmd*/
pmd = fixmap_pmd(FIXADDR_TOP);
/*将bm_pte页表设置为固定映射区开始地址的pmd的第一个页表;*/
pmd_populate_kernel(&init_mm, pmd, bm_pte); pte_offset_fixmap = pte_offset_early_fixmap;
}

1.3 ioremap函数

对于ioremap的使用需要通过early_memremapearly_iounmap进行.

由于对应于ioremap的内存空间是有限的, 所以对于ioremap空间的使用遵照使用结束马上释放的原则. 这就是说early_memremap和early_iounmap必须配对使用并且访问结束必须马上执行unmap

2 临时内核映射

刚才描述的kmap函数不能用于中断处理程序, 因为它可能进入睡眠状态. 如果pkmap数组中没有空闲位置, 该函数会进入睡眠状态, 直至情形有所改善.

因此内核提供了一个备选的映射函数, 其执行是原子的, 逻辑上称为kmap_atomic. 该函数的一个主要优点是它比普通的kmap快速. 但它不能用于可能进入睡眠的代码. 因此, 它对于很快就需要一个临时页的简短代码,是非常理想的.

kmap_atomic的定义在IA-32, PPC, Sparc32上是特定于体系结构的, 但这3种实现只有非常细微的差别. 其原型是相同的.

2.1 kmap_atomic函数

//  http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=4.7#L55
void *kmap_atomic(struct page *page)

page是一个指向高端内存页的管理结构的指针, 而早期的内核中, 增加了一个类型为enum km_typetype参数, 用于指定所需的映射类型

//  http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=2.6.32#L39
void *kmap_atomic(struct page *page, enum km_type type)

而在新的内核中, 删除了这个标识, 但是保留了km_type的最大值KM_TYPE_NR

void *kmap_atomic(struct page *page)
{
unsigned int idx;
unsigned long vaddr;
void *kmap;
int type; preempt_disable();
pagefault_disable();
if (!PageHighMem(page))
return page_address(page); #ifdef CONFIG_DEBUG_HIGHMEM
/*
* There is no cache coherency issue when non VIVT, so force the
* dedicated kmap usage for better debugging purposes in that case.
*/
if (!cache_is_vivt())
kmap = NULL;
else
#endif
kmap = kmap_high_get(page);
if (kmap)
return kmap; type = kmap_atomic_idx_push(); idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
/*
* With debugging enabled, kunmap_atomic forces that entry to 0.
* Make sure it was indeed properly unmapped.
*/
BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
/*
* When debugging is off, kunmap_atomic leaves the previous mapping
* in place, so the contained TLB flush ensures the TLB is updated
* with the new mapping.
*/
set_fixmap_pte(idx, mk_pte(page, kmap_prot)); return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic);

这个函数不会被阻塞, 因此可以用在中断上下文和起亚不能重新调度的地方. 它也禁止内核抢占, 这是有必要的, 因此映射对每个处理器都是唯一的(调度可能对哪个处理器执行哪个进程做变动).

2.2 kunmap_atomic函数

可以通过函数kunmap_atomic取消映射

/*
* Prevent people trying to call kunmap_atomic() as if it were kunmap()
* kunmap_atomic() should get the return value of kmap_atomic, not the page.
*/
#define kunmap_atomic(addr) \
do { \
BUILD_BUG_ON(__same_type((addr), struct page *)); \
__kunmap_atomic(addr); \
} while (0)

这个函数也不会阻塞. 在很多体系结构中, 除非激活了内核抢占, 否则kunmap_atomic根本无事可做, 因为只有在下一个临时映射到来前上一个临时映射才有效. 因此, 内核完全可以”忘掉”kmap_atomic映射, kunmap_atomic也无需做什么实际的事情. 下一个原子映射将自动覆盖前一个映射.

高端内存映射之kmap_atomic固定映射--Linux内存管理(二十一)的更多相关文章

  1. 搜索引擎case︱从搜索序列文本看高端商务车︱统计之都

    朱雪宁(北京大学光华管理学院)               王汉生(北京大学光华管理学院) 摘要:本文对100万搜索引擎用户的13亿搜索序列文本进行探索分析,对高端车用户以及商学院人群做了描述对比,并 ...

  2. Linux内存模型

    http://blog.csdn.net/sunyubo458/article/details/6090946 了解linux的内存模型,或许不能让你大幅度提高编程能力,但是作为一个基本知识点应该熟悉 ...

  3. [国嵌攻略][106][Linux内存管理子系统]

    内存管理子系统 1.虚拟地址与物理地址的映射 2.物理内存的分配 Linux虚拟地址空间分布 设备最后访问的一定是物理地址,但Linux系统中使用的都是虚拟地址.虚拟地址简单的来说就是程序中使用的地址 ...

  4. linux内存管理之malloc、vmalloc、kmalloc的区别

    kmalloc kzalloc vmalloc malloc 和get_free_page()的区别 一.简述 1. kmalloc申请的是较小的连续的物理内存,虚拟地址上也是连续的.kmalloc和 ...

  5. linux内存源码分析 - 页表的初始化

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本文章中系统我们假设为x86下的32位系统,暂且不分析64位系统的页表结构. linux分页 linux下采用四 ...

  6. 非常好的博客!!!linux内存管理概述【转】

    转自:http://blog.csdn.net/bullbat/article/details/7166140 inux内存管理建立在基本的分页机制基础上,在linux内核中RAM的某些部分将会永久的 ...

  7. Linux内存管理(深入理解Linux内核)

    Linux的内存管理,实际上是借助80x86的硬件分段和分页电路,将逻辑地址转化为物理地址的. 物理内存中,有一部分是一直(Permanently)映射给内核使用的,这部分主要用于保存内核的代码,以及 ...

  8. Linux内存管理 (8)malloc

    专题:Linux内存管理专题 关键词:malloc.brk.VMA.VM_LOCK.normal page.special page. 每章问答: malloc()函数是C函数库封装的一个核心函数,对 ...

  9. Linux内存管理 (4)分配物理页面

    专题:Linux内存管理专题 关键词:分配掩码.伙伴系统.水位(watermark).空闲伙伴块合并. 我们知道Linux内存管理是以页为单位进行的,对内存的管理是通过伙伴系统进行. 从Linux内存 ...

随机推荐

  1. 微信小程序自定义导航栏

    微信小程序需要自定义导航栏,特别是左上角的自定义设置,可以设置返回按钮,菜单按钮,配置如下: 1.在app.json的window属性中增加: navigationStyle:custom 顶部导航栏 ...

  2. Primitive Assembly

    I found something in the Specification of OpenGL Version 4.6 (Core Profile): The output of Vertex Sh ...

  3. 配置vscode同步大神玺哥的配置

    1.应用商店下载settings  sync 2.三键 ctrl + shift + p   对话框中输入sync:点击重置 3.ctrl + shift + p  点击下载 4.然后会自动的调整到g ...

  4. Windows系统下安装Redis

    1.首先你要有redis-latest-windws和redisclient-客户端工具 2.在redis-latest-windws文件夹内创建一个批处理文件  start.bat 创建批处理文件的 ...

  5. 【mysql】Date和String的互相转换(DATE_FORMAT & STR_TO_DATE)

    1.Date  ——>  String 使用的函数:DATE_FORMAT(date,format)     date:需要转换的日期       format:格式化的样式 format样式整 ...

  6. Java核心技术及面试指南:视频列表

    如下是本书相关内容的视频列表,会动态更新 第一章 1 视频1.1  JDK和JRE和JVM的区别,安装Java开发环境    1.1.1  第2页 2 视频1.2  编写第一个HelloWorld程序 ...

  7. 带着萌新看springboot源码11(springboot启动原理 源码上)

    通过前面这么多讲解,springboot原理应该也大概有个轮廓了,一些基本的配置,从客户端url到controller(配置一些要用的组件,servlet三大组件,处理器映射器,拦截器,视图解析器这些 ...

  8. 如何合理封装你的轮子、飞机、大炮(以封装OkHttp为例)

    前言 对于程序员来说,很多时候,我们都在造房子,从学会框架或者是学会构建整个项目之后,慢慢的我们就会觉得自己在做的事情是一种重复劳动,很多时候只不过是换个面孔而已.而更快的造房子,造好看的房子可能是进 ...

  9. HBase查询优化

    1.概述 HBase是一个实时的非关系型数据库,用来存储海量数据.但是,在实际使用场景中,在使用HBase API查询HBase中的数据时,有时会发现数据查询会很慢.本篇博客将从客户端优化和服务端优化 ...

  10. Spring Boot (七)MyBatis代码自动生成和辅助插件

    一.简介 1.1 MyBatis Generator介绍 MyBatis Generator 是MyBatis 官方出品的一款,用来自动生成MyBatis的 mapper.dao.entity 的框架 ...