mmap/mumap linux 4k对齐

这段代码实现了一个在Linux上进行内存映射的函数sys_mmap和解映射函数sys_munmap。它们确保了映射的起始地址和大小都是4K对齐。sys_mmap通过打开/dev/mem设备文件映射物理地址到用户空间,而sys_munmap则负责释放这些映射。当munmap遇到错误时,会打印错误信息。
static void *_sys_mmap(k_u64 phys_addr, k_u32 size)
{
    void *virt_addr = NULL;
    void *mmap_addr = NULL;
    k_u32 page_size = sysconf(_SC_PAGESIZE);
    k_u64 page_mask = (page_size - 1);
    k_u32 mmap_size = ((size) + (phys_addr & page_mask) + page_mask) & ~(page_mask);


    if (g_mmap_fd_tmp == 0)
    {
        g_mmap_fd_tmp = open("/dev/mem", O_RDWR | O_SYNC);
    }

    mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_mmap_fd_tmp, phys_addr & ~page_mask);

    if (mmap_addr != (void *)-1)
        virt_addr = (void*)((char*)mmap_addr + (phys_addr & page_mask));
    else
    {
        mapi_aenc_error_trace("mmap addr error: %d %s.\n", mmap_addr, strerror(errno));;
    }

    return virt_addr;
}

static k_s32 _sys_munmap(k_u64 phy_addr, void *virt_addr,k_u32 size)
{
    if (g_mmap_fd_tmp == 0)
    {
        return -1;
    }
    k_u32 ret;

    k_u32 page_size = sysconf(_SC_PAGESIZE);
    k_u64 page_mask = page_size - 1;
    k_u32 mmap_size = ((size) + (phy_addr & page_mask) + page_mask) & ~(page_mask);
    ret = munmap((void *)((k_u64)(virt_addr) & ~page_mask), mmap_size);
    if (ret == -1) {
        mapi_aenc_error_trace("munmap error.\n");
    }

    return 0;
}

上述实现:为了达到linux上mmap 起始地址和size都4k对齐。

### mmap /dev/mem device failed 的原因 #### 1. 偏移值问题 mmap 进入内核后会检查偏移值是否是页的整数倍,如不是,则返回错误,表示无效的参数。这是因为 mmap 要求地址是页对齐的,若传入的物理地址未按页对齐,会导致映射失败[^1]。 #### 2. 物理地址范围问题 会检查映射的物理地址是否超过支持的物理地址范围。如果指定的物理地址超出了系统所支持的物理地址范围,mmap 操作会失败[^1]。 #### 3. 映射类型问题 会检查是否支持私有映射,若系统不支持指定的映射类型(如私有映射),且 CPU 没有相应的 MMU 支持,映射可能会失败。不过一般情况下,有 MMU 的 CPU 默认支持私有映射[^1]。 #### 4. 物理地址权限和范围检查问题 此选项由 CONFIG_STRICT_DEVMEM 控制。若定义了该选项,会对映射的地址进行严格的检查,包括检查映射的物理地址是否是设备独占保留的 IO 映射区域、是否是系统内存、物理内存的访问权限是否符合要求等。若不符合要求,映射会失败;若没有定义该选项,则不进行这些检查[^1]。 #### 5. 权限问题 访问 `/dev/mem` 需要特殊权限,通常需要以 root 权限运行程序。如果程序没有足够的权限,mmap 操作会失败[^1]。 ### 解决办法 #### 1. 确保地址页对齐 在进行 mmap 操作前,需要计算页对齐的物理地址和页内偏移量。以下是示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #define PAGE_SIZE 4096 int main() { int fd; void *mapped_addr; off_t physical_address = 0x12345678; // 示例物理地址,需替换为实际地址 // 打开 /dev/mem 设备文件 fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { perror("open"); return -1; } // 计算页对齐的物理地址 off_t page_offset = physical_address & ~(PAGE_SIZE - 1); off_t offset_in_page = physical_address - page_offset; // 使用 mmap 进行内存映射 mapped_addr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_offset); if (mapped_addr == MAP_FAILED) { perror("mmap"); close(fd); return -1; } // 后续操作... // 解除映射 if (munmap(mapped_addr, PAGE_SIZE) == -1) { perror("munmap"); } // 关闭设备文件 close(fd); return 0; } ``` #### 2. 检查物理地址范围 在使用 mmap 之前,需要确保指定的物理地址在系统支持的物理地址范围内。可以参考系统文档或咨询硬件供应商来确定物理地址范围。 #### 3. 检查映射类型 确保指定的映射类型(如私有映射或共享映射)是系统支持的。一般情况下,有 MMU 的 CPU 默认支持私有映射。 #### 4. 配置 CONFIG_STRICT_DEVMEM 如果 CONFIG_STRICT_DEVMEM 导致映射失败,可以通过修改内核配置来解决。若不需要严格的地址检查,可以在编译内核时不定义该选项。配置路径为:Kernel hacking ---> [ ] Filter access to /dev/mem。 #### 5. 以 root 权限运行程序 确保程序以 root 权限运行,可以使用 `sudo` 命令来提升权限。例如: ```sh sudo ./your_program ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaopengsun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值