qq_37106574 2023-03-27 21:32 采纳率: 20%
浏览 77
已结题

关于mmap将物理空间映射到用户空间再落盘时使用O_DIRECT方式会提示bad address的问题如何解决?

linux中将外设物理地址mmap映射到用户空间,通过在用户进程中访问映射的虚拟地址来读取外设的数据,并落盘,落盘时open函数使用O_DIRECT方式,直接write数据会报bad address错误,但如果使用posix_memalign申请一块空间来中转数据,再write就能写进去,这是为什么呢?
因为项目追求速度要快,所以使用O_DIRECT的方式,如果在途中还需要memcpy也会影响效率,有其他方法可以不经过缓存直接落盘吗?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>

#define SHR_MEMSIZE            0x800000
#define MEM_CLEAR               0x0
#define MEM_RESET               0x1
#define MEM_DEV_FILENAME       "/dev/mychrmem"

int main()
{
    int         fd;
    int         file_fd;
    ssize_t        ret;
    char        *shm = NULL;
    char        *rev_buf;

//    posix_memalign((void**)&rev_buf, 512, SHR_MEMSIZE);

    fd = open(MEM_DEV_FILENAME, O_RDWR);
    if (fd < 0) {
        printf("open(): %s\n", strerror(errno));
        return -1;
    }

    file_fd = open("/mnt/test",O_WRONLY|O_CREAT|__O_DIRECT,0666);
    if (file_fd < 0)
    {
        perror("open");
        return -1;
    }

    shm = mmap(NULL, SHR_MEMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == shm) {
        printf("mmap: %s\n", strerror(errno));
    }

    printf("Before Write, shm = %s\n", shm); 

    strcpy(shm,"User write to share memory!");

    printf("After write, shm = %s\n", shm);

    while(1)
    {
//        memcpy(rev_buf, shm, SHR_MEMSIZE);       //中转数据能成功,但影响效率
//        ret = write(file_fd, rev_buf, SHR_MEMSIZE);

        ret = write(file_fd, shm, SHR_MEMSIZE);      //直接写会提示bad address

        printf("mem addr is 0x%lx\n",(long)shm);
        if(ret==-1)
        {
            perror("write");
        }
        else
        {
            printf("write len 0x%lx\n",ret);
        }
    }

    munmap(shm, SHR_MEMSIZE);
    close(file_fd); 
    close(fd);
    return 0;    
}


  • 写回答

6条回答 默认 最新

  • JoseKe 2023-03-27 22:06
    关注
    该回答引用ChatGPT
    问题分析:
    在使用mmap将物理空间映射到用户空间后,通过直接写入文件的方式落盘时,使用O_DIRECT方式会提示bad address错误。但是如果使用posix_memalign申请一块空间来中转数据,再write就能写进去。为什么会出现这种情况呢?
    原因分析:
    O_DIRECT方式要求数据必须对齐到块大小,否则会报错。而mmap映射的虚拟地址并不一定是对齐的,因此直接使用O_DIRECT方式写入会出现bad address错误。
    而使用posix_memalign申请一块空间来中转数据,可以保证数据对齐到块大小,因此可以成功写入。
    解决方法:
    1. 使用posix_memalign申请一块空间来中转数据,再write。
    2. 使用O_DIRECT方式写入时,需要保证数据对齐到块大小。可以使用posix_memalign申请一块空间,将mmap映射的虚拟地址复制到该空间中,再使用O_DIRECT方式写入该空间即可。
    代码示例:

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #define PAGE_SIZE 4096
    #define FILE_SIZE (PAGE_SIZE * 10)
    int main()
    {
    int fd, file_fd;
    char *buf, *aligned_buf;
    size_t size = FILE_SIZE;
    // 打开文件
    file_fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, 0666);
    if (file_fd < 0) {
    perror("open");
    exit(1);
    }
    // 分配内存
    if (posix_memalign((void **)&buf, PAGE_SIZE, size)) {
    perror("posix_memalign");
    exit(1);
    }
    // 映射物理地址到用户空间
    fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
    perror("open");
    exit(1);
    }
    aligned_buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x10000000);
    if (aligned_buf == MAP_FAILED) {
    perror("mmap");
    exit(1);
    }
    // 将物理地址映射到的数据复制到对齐的内存中
    memcpy(buf, aligned_buf, size);
    // 写入文件
    if (write(file_fd, buf, size) < 0) {
    perror("write");
    exit(1);
    }
    // 释放资源
    munmap(aligned_buf, size);
    free(buf);
    close(file_fd);
    close(fd);
    return 0;
    }
    评论

报告相同问题?

问题事件

  • 系统已结题 4月4日
  • 修改了问题 3月27日
  • 创建了问题 3月27日

悬赏问题

  • ¥100 复现论文:matlab仿真代码编写
  • ¥15 esp32驱动GC9A01循环播放视频
  • ¥15 惠普360g9的最新bios
  • ¥30 这个功能用什么软件发合适?
  • ¥60 微信小程序,取消订单,偶尔订单没有改变状态
  • ¥15 用pytorch实现PPO算法
  • ¥15 关于调制信号的星座图?
  • ¥30 前端传参时,后端接收不到参数
  • ¥15 这是有什么问题吗,我检查许可证了但是显示有呢
  • ¥15 机器学习预测遇到的目标函数问题