Linux ioremap分析

Linux ioremap分析ioremap 将物理地址转换为虚拟地址 nbsp gt ioremap addr size arch arm64 include asm io h gt ioremap addr size pgprot PROT DEVICE nGnRE definePROT DEVICE nGnRE PROT DEFAULT PTE PXN PTE UXN

ioremap()将物理地址转换为虚拟地址 

->ioremap(addr, size) arch/arm64/include/asm/io.h
->__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
{

#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN |
PTE_DIRTY | PTE_WRITE | 
PTE_ATTRINDX(MT_DEVICE_nGnRE))
}
->__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot) arch/arm64/mm/ioremap.c
->_ioremap_caller(phys_addr_t phys_addr, size_t size, pgprot_t prot, void *caller)
{

若页表的大小为4K, offset是相对于页表的offsset
unsigned long offset = phys_addr & ~PAGE_MASK;

物理地址的分配以页表对齐,size的大小为PAGE_ALIGN(size + offset)
phys_addr &= PAGE_MASK;
size = PAGE_ALIGN(size + offset);
last_addr = phys_addr + size – 1;

/*如果物理地址在RAM的区间内,则不允许做map操作*/
if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))
return NULL;

在linux虚拟内存中找到一段的mem空间,用vm_struct数据结构进行描述
struct vm_struct *area;
area = get_vm_area_caller(size, VM_IOREMAP, caller); arch/arm64/mm/ioremap.c

VMALLOC_START和VMALLOC_END为VMALLOC的mem空间,定义在arch/arm64/include/asm/pgtable.h中
定义如下:
{

#define VMALLOC_START (MODULES_END)
#define VMALLOC_END   (PAGE_OFFSET – PUD_SIZE – VMEMMAP_SIZE – SZ_64K)
}

__get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
                       NUMA_NO_NODE, GFP_KERNEL, caller);
{

分配一个struct vm_struct的结构体
struct vm_struct *area;
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);

在vmalloc地址空间内找到一块未分配的虚拟空间
va = alloc_vmap_area(size, align, start, end, node, gfp_mask)
{

struct vmap_area *va;
va = kmalloc_node(sizeof(struct vmap_area),gfp_mask & GFP_RECLAIM_MASK, node);

如果不满足从free_vmap_cache中分配mem的条件
if (!free_vmap_cache || size < cached_hole_size ||
vstart < cached_vstart || align < cached_align)
{

nocache:
cached_hole_size = 0;
free_vmap_cache = NULL;
}

cached_vstart为VMSTART
cached_vstart = vstart;
cached_align = align;

如果free_vmap_cache存在, 找到应经被free掉的vmap_area空间
if (free_vmap_cache) 
{

                first = rb_entry(free_vmap_cache, struct vmap_area, rb_node);
                addr = ALIGN(first->va_end, align);
                if (addr < vstart)
                        goto nocache;
                if (addr + size < addr)
                        goto overflow;

} else 
{

从vmap_area_root的根节点开始遍历,vmap_area_root为已经分配的mem组成的红黑树

addr = ALIGN(vstart, align);
n = vmap_area_root.rb_node;
while (n) 
{

struct vmap_area *tmp;

tmp = rb_entry(n, struct vmap_area, rb_node);
if (tmp->va_end >= addr) {

first = tmp;
if (tmp->va_start <= addr)
break;
n = n->rb_left;
} else
n = n->rb_right;
                }

如果first不存在,则说明红黑树中还没有节点,或者则从VMSTART开始分配
if (!first)
                        goto found;
}

从vmap_area_list中的
while (addr + size > first->va_start && addr + size <= vend) 
{

                if (addr + cached_hole_size < first->va_start)
                        cached_hole_size = first->va_start – addr;
                addr = ALIGN(first->va_end, align);

                if (addr + size < addr)
                        goto overflow;

                if (list_is_last(&first->list, &vmap_area_list))
                        goto found;

                first = list_next_entry(first, list);
}

found:
va->va_start = addr;
va->va_end = addr + size;
va->flags = 0;

将va插入到红黑树节点中
__insert_vmap_area(va);
free_vmap_cache = &va->rb_node;
}


初始化struct vm_struct结构体,用来描述虚拟空间
setup_vmalloc_vm(area, va, flags, caller);
{

vm->flags = flags;
vm->addr = (void *)va->va_start;
vm->size = va->va_end – va->va_start;
vm->caller = caller;
va->vm = vm;
va->flags |= VM_VM_AREA;
}
}

如果找到一块vmalloc的mem空间可以进行ioremap操作,则进行ioremap操作
addr = (unsigned long)area->addr;
area->phys_addr = phys_addr;

lib/ioremap.c
err = ioremap_page_range(addr, addr + size, phys_addr, prot);
{

ioremap对应的虚拟空间的起始地址
start = addr;

物理地址减去起始的虚拟地址
phys_addr -= addr;

根据虚拟地址找到页全局目录
pgd = pgd_offset_k(addr);
{

#define pgd_offset_k(addr)      pgd_offset(&init_mm, addr)
#define pgd_offset(mm, addr) (pgd_offset_raw((mm)->pgd, (addr)))
#define pgd_offset_raw(pgd, addr) ((pgd) + pgd_index(addr))
#define pgd_index(addr)         (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD – 1))
}

循环映射直到addr==end, 即所有的pgd都被遍历到
do 
{

next为下一个pdg对应的虚拟地址
next = pgd_addr_end(addr, end);

addr为pgd对应的start地址,next为pgd对应的end地址,由于篇幅的问题,
我们忽略掉pud和pmd,直接分析pgd->pte的映射结构。
err = ioremap_pte_range(pgd, addr, next, phys_addr+addr, prot);
{

物理页框号
pfn = phys_addr >> PAGE_SHIFT;

获取页表项
pte = pte_alloc_kernel(pmd, addr);

遍历一个pgd对应的地址空间
do 
{

建立页表,lib/ioremap.c, 该函数的第四个参数pfn_pte(pfn, prot)为由
pfn和prot所构建的页表
set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
{

set_pte(ptep, pte);
{

*ptep = pte;
}
}
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
}
if (err)
break;

} while (pgd++, addr = next, addr != end);
}
}





































































































































































































版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/202284.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午7:29
下一篇 2026年3月20日 上午7:30


相关推荐

  • lsof命令无效

    lsof命令无效1.问题描述在centos下,无法使用命令lsof,出现以下信息:[plain] viewplain copy# lsof -i:3690  -bash: lsof: command not found  2.解决方法我们可以通过yum来安装:[plain] viewplain copy# yum install lsof  本人操作如下:[plain] viewplain copy

    2025年6月7日
    7
  • 【手把手教你在 OpenClaw 本地部署集成 DeepSeek 模型(DeepSeek-V3 / R1 / Coder 全覆盖)】

    【手把手教你在 OpenClaw 本地部署集成 DeepSeek 模型(DeepSeek-V3 / R1 / Coder 全覆盖)】

    2026年3月13日
    3
  • linux下更新pip3「建议收藏」

    本机linux下有python(表示python2)和python3,其中python3对应pip3。1.首先下载pip#wgethttps://bootstrap.pypa.io/get-pip.py如果没有wget,先安装wget#yum-yinstallwget在python3下执行脚本#python3get-pip.py#执行成功后查看版本…

    2022年4月11日
    231
  • Spring Boot 配置文件 yml与properties (二)

    Spring Boot 配置文件 yml与properties (二)1 配置文件 SpringBoot 使用一个全局的配置文件 配置文件名是固定的 application properties application yml 配置文件的作用 修改 SpringBoot 自动配置的默认值 SpringBoot 在底层都给我们自动配置好 官方语法规 YAML YAMLAin tMarkupLangu YAMLAMarkupL

    2026年3月19日
    37
  • python求平均值的怎么编写,python 怎么求平均值[通俗易懂]

    python求平均值的怎么编写,python 怎么求平均值[通俗易懂]python求平均值的方法:首先新建一个python文件;然后初始化sum总和的值;接着循环输入要计算平均数的数,并计算总和sum的值;最后利用“总和/数量”的公式计算出平均数即可。本文操作环境:Windows7系统,python3.5版本,DellG3电脑。首先我们先来了解一下计算平均数的IPO模式.输入:待输入计算平均数的数。处理:平均数算法输出:平均数明白了程序的IPO模式之后,我们打开本…

    2025年6月21日
    6
  • c语言中switch用法举例

    c语言中switch用法举例例 1 如本文中描述的 switch 语法 switch 是多个 ifelse 的组合 其形式上与 if 的语法结构很像 if 表达式 switch 表达式 case0 表达式的各种取值 冒号 这跟我们平常列举各种情况的写法类似 case1 当表达式的多个取值的结果都一样的时 可以写在一起 在最后写 break case3 cout lt lt 情形 1 break case2 cout lt lt 情形 2 bre

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号