Linux内核memremap设置为uncahche

Linux内核memremap设置为uncache

在Linux内核中,将内存映射设置为uncacheable(不可缓存)通常用于访问设备寄存器或需要严格内存顺序的场景。以下是实现方法和关键步骤:


1. 使用 memremap API

  • 通过 memremap() 函数指定 MEMREMAP_UC 标志实现不可缓存映射:

    1
    2
    3
    #include <linux/io.h>

    void *addr = memremap(phys_addr, size, MEMREMAP_UC);
    • 参数说明
      • phys_addr: 要映射的物理地址起始位置
      • size: 映射区域的大小
      • MEMREMAP_UC: 映射为不可缓存(Uncacheable)
  • 返回值

    • 成功:映射后的虚拟地址指针(void __iomem *类型)
    • 失败:NULL(需检查错误)
  • 解除映射

    1
    memunmap(addr); // 使用后解除映射

2. 使用 ioremap API(替代方案)

  • ioremap() 默认映射为不可缓存(等效于 MEMREMAP_UC):
    1
    void *addr = ioremap(phys_addr, size);
    • 解除映射使用 iounmap(addr)

3. 关键注意事项

  • 访问方式

    • 必须使用内核提供的I/O函数访问映射区域:
      1
      2
      u32 val = readl(addr);          // 读32位数据
      writel(value, addr + offset); // 写32位数据
      其他函数:readb()/writeb()(8位)、readw()/writew()(16位)。
  • 缓存一致性

    • 不可缓存映射绕过CPU缓存,确保每次访问直接读写物理内存。
    • 适用于设备寄存器(值可能随时变化)或DMA缓冲区(需避免缓存一致性问题)。
  • 性能影响

    • 频繁访问时性能较低(无缓存加速),仅必要时使用。
  • 地址对齐

    • 物理地址和大小通常需按页对齐(PAGE_SIZE,如4KB)。
  • 资源管理

    • 确保在模块退出或错误路径中调用 memunmap()iounmap() 释放资源。

4. 完整示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>

#define PHYS_ADDR 0xFE000000 // 目标物理地址
#define REG_SIZE 0x1000 // 映射区域大小(4KB)

static void __iomem *uc_map;

static int __init my_init(void)
{
// 映射为不可缓存
uc_map = memremap(PHYS_ADDR, REG_SIZE, MEMREMAP_UC);
if (!uc_map) {
pr_err("memremap failed\n");
return -ENOMEM;
}

// 示例:读取设备寄存器
u32 reg_val = readl(uc_map + 0x10);
pr_info("Register value: 0x%x\n", reg_val);

return 0;
}

static void __exit my_exit(void)
{
if (uc_map) {
// 解除映射
memunmap(uc_map);
pr_info("Unmapped UC region\n");
}
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

5. 适用场景

  • 设备寄存器访问(如硬件状态寄存器)
  • DMA缓冲区(避免CPU缓存导致的脏数据问题)
  • 特殊内存区域(如帧缓冲区、非一致性内存)

通过正确使用 MEMREMAP_UCioremap(),可确保内核安全、高效地访问需要严格内存顺序的设备内存区域。