• 应用层mmap和驱动层mmap之间的联系


    前言

            本文是边整理思路,边测代码整理出来的。是笔记。

            mmap的函数如下:

    1. void *mmap(void *addr, size_t length, int prot, int flags,
    2.                   int fd, off_t offset);

            默认情况下,addr值为空,中间的 length, prot, flags,fd 四个参数基本不会有争议。本节讨论addr和offset这两个参数,并书写测试代码进行测试验证。

    最后一个参数看名字表示偏移,它是哪里到哪里的偏移呢?它是传输到内核的mmap函数以后,会变成什么呢?

    一 错误的打开方式?

    如下代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #define IMAGE_SIZE 1280*800
    12. #define DEV_NAME "/dev/video1"
    13. int main(int argc,char *argv[])
    14. {
    15. int i = 0;
    16. char *vaddr = NULL;
    17. char *vaddr_bak = NULL;
    18. char *name;
    19. int fd = 0;
    20. if(argc < 2){
    21. name = DEV_NAME;
    22. }else{
    23. name = argv[1];
    24. }
    25. fd = open(name,O_RDONLY);
    26. if(fd < 0){
    27. perror("open");
    28. return -1;
    29. }
    30. printf("open %s ok\n",name);
    31. for(i = 0;i < 5;i++){
    32. vaddr = mmap(NULL, IMAGE_SIZE,
    33. PROT_READ | PROT_WRITE, MAP_SHARED,
    34. fd, i*IMAGE_SIZE);
    35. printf("vaddr_%d = %p\n",i,vaddr);
    36. if(vaddr_bak != NULL){
    37. printf("%d\n",abs(vaddr_bak - vaddr));
    38. }
    39. vaddr_bak = vaddr;
    40. }
    41. close(fd);
    42. return 0;
    43. }

    输出结果,

    1. root@hehe:~# ./mmap
    2. open /dev/video1 ok
    3. vaddr_0 = 0xffffffff
    4. vaddr_1 = 0xffffffff
    5. vaddr_2 = 0xffffffff
    6. vaddr_3 = 0xffffffff
    7. vaddr_4 = 0xffffffff
    8. root@hehe:~#

            先不说mmap参数有没有问题,open打开方式O_RDONLY,将用户空间的内存映射到内核空间为什么不能是只读映射呢?当然可以了。

    这个问题有两个解决方案:1)O_RDONLY换成O_RDWR 2)mmap中PROT_WRITE去掉。我们先尝试第一种方案:

    方案1:O_RDONLY换成O_RDWR

            修改open打开方式如下所示:

    fd = open(name,O_RDWR);

    再次运行:结果看上去是没错了。映射地址的间距是1024000,

    1. root@hehe:~# ./mmap
    2. open /dev/video1 ok
    3. vaddr_0 = 0x76ce2000
    4. vaddr_1 = 0x76be8000
    5. 1024000
    6. vaddr_2 = 0x76aee000
    7. 1024000
    8. vaddr_3 = 0x769f4000
    9. 1024000
    10. vaddr_4 = 0x768fa000
    11. 1024000
    12. root@hehe:~#

    方案二:mmap中PROT_WRITE去掉

    此时的open如下:

    fd = open(name,O_RDONLY);

    mmap如下:

    1. vaddr = mmap(NULL, IMAGE_SIZE,
    2. PROT_READ, MAP_SHARED,
    3. fd, i*IMAGE_SIZE);

    测试结果:

    1. open /dev/video1 ok
    2. vaddr_0 = 0x76d33000
    3. vaddr_1 = 0x76c39000
    4. 1024000
    5. vaddr_2 = 0x76b3f000
    6. 1024000
    7. vaddr_3 = 0x76a45000
    8. 1024000
    9. vaddr_4 = 0x7694b000
    10. 1024000
    11. root@hehe:~#

            总结一下,就是open中读写方式要和mmap中设置的读写方式保持一致。

    二 mmap的offset设置为自定义的值怎么样?

    这个暂时还没有比较有逻辑的测试方法。

    三 munmap函数测试

    int munmap(void *addr, size_t length);
    

    参数addr:mmap返回的虚拟地址

    参数length:mmap映射时的地址长度

    example:

    1. for(i = 0;i < 5;i++){
    2. printf("munmap = %d\n",munmap(vaddr_array[i].vaddr,vaddr_array[i].length));
    3. }

    四 验证映射的地址的准确性

            在内核中申请的指针数组映射到用户空间的指针数组,如何保证他们肯定是一一对应的呢?在内核中申请完内存后,给他们一个初值,如下所示:通过sprintf(new_node->buf,"buf %d",i);,给每个内存块写一个字符串。

    1. static int csi_dev_node_array_init(struct csi_dev_ *cm_dev)
    2. {
    3. int i = 0;
    4. struct buf_node *new_node;
    5. for(i = 0;i < cm_dev->frame_count;i++){
    6. new_node = kmalloc(sizeof(struct buf_node),GFP_KERNEL);
    7. if(IS_ERR(new_node)){
    8. DEBUG_CM("kmalloc error");
    9. return -ENOMEM;
    10. }
    11. cm_dev->node_array[i] = new_node;
    12. INIT_LIST_HEAD(&new_node->node);
    13. new_node->buf = kmalloc(CSI_DEV_IMAGE_SIZE,GFP_KERNEL|GFP_DMA);
    14. if(IS_ERR(new_node->buf)){
    15. kfree(new_node);
    16. DEBUG_CM("kmalloc error");
    17. return -ENOMEM;
    18. }
    19. new_node->index = i;
    20. sprintf(new_node->buf,"buf %d",i);
    21. list_add_tail(&new_node->node,&cm_dev->ready_list);
    22. }
    23. return 0;
    24. }

            在用户空间:打印映射后的内存       

    1. for(i = 0;i < 5;i++){
    2. printf("vaddr_array[%d].vaddr = %s\n",i,vaddr_array[i].vaddr);
    3. }

            经过测试后:保证了内存映射的准确性。至少映射对了地方。

    1. -------------------
    2. vaddr_array[0].vaddr = buf 0
    3. vaddr_array[1].vaddr = buf 1
    4. vaddr_array[2].vaddr = buf 2
    5. vaddr_array[3].vaddr = buf 3
    6. vaddr_array[4].vaddr = buf 4

    五 本次实验的完整的用户空间代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #define IMAGE_SIZE 1280*800
    14. #define DEV_NAME "/dev/video1"
    15. #define handle_error(msg) \
    16. do { perror(msg); exit(EXIT_FAILURE); } while (0)
    17. struct vaddr_mmap_{
    18. char *vaddr;
    19. int length;
    20. int offset;
    21. };
    22. struct vaddr_mmap_ vaddr_array[5];
    23. int main(int argc,char *argv[])
    24. {
    25. int i = 0;
    26. char *vaddr = NULL;
    27. char *vaddr_bak = NULL;
    28. char *name;
    29. int fd = 0;
    30. struct v4l2_buffer buf;
    31. int len;
    32. if(argc < 2){
    33. name = DEV_NAME;
    34. }else{
    35. name = argv[1];
    36. }
    37. fd = open(name,O_RDWR);
    38. if(fd < 0){
    39. perror("open");
    40. return -1;
    41. }
    42. printf("open %s ok\n",name);
    43. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    44. buf.memory = V4L2_MEMORY_MMAP;
    45. for (buf.index = 0; buf.index < 5; buf.index++) {
    46. printf("----------%d---------\n",buf.index);
    47. if(ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0){
    48. handle_error("VIDIOC_QUERYBUF error");
    49. }
    50. printf("buf.length = %d,buf.index = %d,buf.m.offset = %d\n",
    51. buf.length,buf.index,buf.m.offset);
    52. vaddr = mmap(NULL, buf.length,
    53. PROT_READ | PROT_WRITE, MAP_SHARED,
    54. fd, buf.m.offset);
    55. if (MAP_FAILED == vaddr) {
    56. handle_error("mmap error");
    57. }
    58. if(vaddr_bak != NULL){
    59. printf("%d\n",abs(vaddr_bak - vaddr));
    60. }
    61. printf("vaddr = %p\n",vaddr);
    62. vaddr_bak = vaddr;
    63. vaddr_array[buf.index].vaddr = vaddr;
    64. vaddr_array[buf.index].length = buf.length;
    65. vaddr_array[buf.index].offset = buf.m.offset;
    66. printf("-------------------\n");
    67. }
    68. for(i = 0;i < 5;i++){
    69. printf("vaddr_array[%d].vaddr = %s\n",i,vaddr_array[i].vaddr);
    70. }
    71. for(i = 0;i < 5;i++){
    72. printf("munmap = %d\n",munmap(vaddr_array[i].vaddr,vaddr_array[i].length));
    73. }
    74. close(fd);
    75. return 0;
    76. }

    结束

  • 相关阅读:
    docker安装RabbitMQ及安装延迟插件
    Linux之epoll理解
    【CTF_SQL】[极客大挑战 2019]LoveSQL 1
    JavaScript 71 JavaScript JSON 71.9 JSON 服务器
    考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Matlab&Python代码)
    MySQL定位CPU利用率过高的SQL方法
    请说一下浏览器从输入URL 到页面展示这个过程中都经历了什么?你能答出来吗?
    Programming abstractions in C阅读笔记:p176-p178
    大数据Flink(六十七):SQL & Table 简介及运行环境
    SSL证书优惠购买,HTTPS证书双11价格
  • 原文地址:https://blog.csdn.net/yueni_zhao/article/details/127434760