本文是边整理思路,边测代码整理出来的。是笔记。
mmap的函数如下:
- void *mmap(void *addr, size_t length, int prot, int flags,
- int fd, off_t offset);
默认情况下,addr值为空,中间的 length, prot, flags,fd 四个参数基本不会有争议。本节讨论addr和offset这两个参数,并书写测试代码进行测试验证。
最后一个参数看名字表示偏移,它是哪里到哪里的偏移呢?它是传输到内核的mmap函数以后,会变成什么呢?
如下代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define IMAGE_SIZE 1280*800
- #define DEV_NAME "/dev/video1"
- int main(int argc,char *argv[])
- {
- int i = 0;
- char *vaddr = NULL;
- char *vaddr_bak = NULL;
- char *name;
- int fd = 0;
- if(argc < 2){
- name = DEV_NAME;
- }else{
- name = argv[1];
- }
- fd = open(name,O_RDONLY);
- if(fd < 0){
- perror("open");
- return -1;
- }
- printf("open %s ok\n",name);
- for(i = 0;i < 5;i++){
- vaddr = mmap(NULL, IMAGE_SIZE,
- PROT_READ | PROT_WRITE, MAP_SHARED,
- fd, i*IMAGE_SIZE);
- printf("vaddr_%d = %p\n",i,vaddr);
- if(vaddr_bak != NULL){
- printf("%d\n",abs(vaddr_bak - vaddr));
- }
- vaddr_bak = vaddr;
- }
- close(fd);
- return 0;
- }
-
输出结果,
- root@hehe:~# ./mmap
- open /dev/video1 ok
- vaddr_0 = 0xffffffff
- vaddr_1 = 0xffffffff
- vaddr_2 = 0xffffffff
- vaddr_3 = 0xffffffff
- vaddr_4 = 0xffffffff
- 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,
- root@hehe:~# ./mmap
- open /dev/video1 ok
- vaddr_0 = 0x76ce2000
- vaddr_1 = 0x76be8000
- 1024000
- vaddr_2 = 0x76aee000
- 1024000
- vaddr_3 = 0x769f4000
- 1024000
- vaddr_4 = 0x768fa000
- 1024000
- root@hehe:~#
-
方案二:mmap中PROT_WRITE去掉
此时的open如下:
fd = open(name,O_RDONLY);
mmap如下:
- vaddr = mmap(NULL, IMAGE_SIZE,
- PROT_READ, MAP_SHARED,
- fd, i*IMAGE_SIZE);
测试结果:
- open /dev/video1 ok
- vaddr_0 = 0x76d33000
- vaddr_1 = 0x76c39000
- 1024000
- vaddr_2 = 0x76b3f000
- 1024000
- vaddr_3 = 0x76a45000
- 1024000
- vaddr_4 = 0x7694b000
- 1024000
- root@hehe:~#
总结一下,就是open中读写方式要和mmap中设置的读写方式保持一致。
这个暂时还没有比较有逻辑的测试方法。
int munmap(void *addr, size_t length);
参数addr:mmap返回的虚拟地址
参数length:mmap映射时的地址长度
example:
- for(i = 0;i < 5;i++){
- printf("munmap = %d\n",munmap(vaddr_array[i].vaddr,vaddr_array[i].length));
- }
在内核中申请的指针数组映射到用户空间的指针数组,如何保证他们肯定是一一对应的呢?在内核中申请完内存后,给他们一个初值,如下所示:通过sprintf(new_node->buf,"buf %d",i);,给每个内存块写一个字符串。
- static int csi_dev_node_array_init(struct csi_dev_ *cm_dev)
- {
- int i = 0;
- struct buf_node *new_node;
-
- for(i = 0;i < cm_dev->frame_count;i++){
- new_node = kmalloc(sizeof(struct buf_node),GFP_KERNEL);
- if(IS_ERR(new_node)){
- DEBUG_CM("kmalloc error");
- return -ENOMEM;
- }
- cm_dev->node_array[i] = new_node;
-
- INIT_LIST_HEAD(&new_node->node);
- new_node->buf = kmalloc(CSI_DEV_IMAGE_SIZE,GFP_KERNEL|GFP_DMA);
- if(IS_ERR(new_node->buf)){
- kfree(new_node);
- DEBUG_CM("kmalloc error");
- return -ENOMEM;
- }
- new_node->index = i;
- sprintf(new_node->buf,"buf %d",i);
- list_add_tail(&new_node->node,&cm_dev->ready_list);
- }
- return 0;
- }
在用户空间:打印映射后的内存
- for(i = 0;i < 5;i++){
- printf("vaddr_array[%d].vaddr = %s\n",i,vaddr_array[i].vaddr);
- }
经过测试后:保证了内存映射的准确性。至少映射对了地方。
- -------------------
- vaddr_array[0].vaddr = buf 0
- vaddr_array[1].vaddr = buf 1
- vaddr_array[2].vaddr = buf 2
- vaddr_array[3].vaddr = buf 3
- vaddr_array[4].vaddr = buf 4
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define IMAGE_SIZE 1280*800
- #define DEV_NAME "/dev/video1"
- #define handle_error(msg) \
- do { perror(msg); exit(EXIT_FAILURE); } while (0)
- struct vaddr_mmap_{
- char *vaddr;
- int length;
- int offset;
- };
- struct vaddr_mmap_ vaddr_array[5];
- int main(int argc,char *argv[])
- {
- int i = 0;
- char *vaddr = NULL;
- char *vaddr_bak = NULL;
- char *name;
- int fd = 0;
- struct v4l2_buffer buf;
- int len;
-
- if(argc < 2){
- name = DEV_NAME;
- }else{
- name = argv[1];
- }
- fd = open(name,O_RDWR);
- if(fd < 0){
- perror("open");
- return -1;
- }
- printf("open %s ok\n",name);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- for (buf.index = 0; buf.index < 5; buf.index++) {
- printf("----------%d---------\n",buf.index);
- if(ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0){
- handle_error("VIDIOC_QUERYBUF error");
- }
- printf("buf.length = %d,buf.index = %d,buf.m.offset = %d\n",
- buf.length,buf.index,buf.m.offset);
- vaddr = mmap(NULL, buf.length,
- PROT_READ | PROT_WRITE, MAP_SHARED,
- fd, buf.m.offset);
- if (MAP_FAILED == vaddr) {
- handle_error("mmap error");
- }
- if(vaddr_bak != NULL){
- printf("%d\n",abs(vaddr_bak - vaddr));
- }
- printf("vaddr = %p\n",vaddr);
- vaddr_bak = vaddr;
- vaddr_array[buf.index].vaddr = vaddr;
- vaddr_array[buf.index].length = buf.length;
- vaddr_array[buf.index].offset = buf.m.offset;
- printf("-------------------\n");
- }
- for(i = 0;i < 5;i++){
- printf("vaddr_array[%d].vaddr = %s\n",i,vaddr_array[i].vaddr);
- }
- for(i = 0;i < 5;i++){
- printf("munmap = %d\n",munmap(vaddr_array[i].vaddr,vaddr_array[i].length));
- }
- close(fd);
- return 0;
- }