mmap函数

void *mmap(void *addr,size_t len, int prot, int flags, int fd, off_t offset)
int munmao(void *start, size_t length)

addr:    指定映射的起始地址,通常为NULL,由系统指定
len :  文件中映射到内存的长度
port:
            PORT_EXEC:映射区可被执行;
            PORT_READ:映射区可被读取;
            PORT_WRITE:映射区可被写入;
flags:
            MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享
            MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write),
                                    对此区域的修改不会写回原文件。
offset:

   映射文件中的偏移量

 

 

 

#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/mman.h>
 
int main()
{
    int fd;
    char *start;
    char buf[100];
 
    /* open the device */
    fd = open("testfile",O_RDWR);
        
    start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    
    /* read the data */
    strcpy(buf,start);
    printf("buf = %s/n",buf);    
 
    /* write data */
    strcpy(start,"Buf Is Not Null!");
    
    munmap(start,100); /* unmap */
    close(fd);  
    
    return 0;    
}

 

虚拟内存区域

  虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。一个进程的内存映象由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。

一个进程的内存区域可以通过查看:cat /proc/pid/maps
08048000-0804f000 r-xp 00000000 08:01 573748 /sbin/rpc.statd #text
0804f000-08050000 rw-p 00007000 08:01 573748 /sbin/rpc.statd #data
08050000-08055000 rwxp 00000000 00:00 0 #bss
040000000-40015000 r-xp 00000000 08:01 933965 /lib/ld2.3.2.so #text
40015000-40016000 rw-p 00014000 08:01 933965 /lib/ld-2.3.2.so #data

 

 Linux内核使用结构vm_area_struct(<linux/mm_types.h>)来描述虚拟内存区域,其中几个主要成员如下:

  1)unsigned long vm_start   虚拟内存区域起始地址
  2)unsigned long vm_end    虚拟内存区域结束地址

  3)unsigned long vm_flags  该区域的标记。如:VM_IO和VM_RESERVED。VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump )中,VM_RESERVED标志内存区域不能被换出

 

mmap设备操作
      映射一个设备是指把用户空间(虚拟地址)的一段地址关联到设备内存(物理地址)上。当程序读写这段用户空间的地址时,它实际上是在访问设备。

  mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表

int (*mmap) (struct file *, struct vm_area_struct *)

 

页表的建立

两种方法:
     1)使用remap_pfn_range一次建立所有页表;
     2)使用nopage VMA方法每次建立一个页表。

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,unsigned long pfn, unsigned long size, pgprot_t prot)

     vma:         虚拟内存区域指针
     virt_addr:   虚拟地址的起始值

     pfn:         要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到。
     size:        要映射的区域的大小。
     prot:        VMA的保护属性。

 

实例

int memdev_mmap(struct file*filp, struct vm_area_struct *vma)
{
  Vma->vm_flags |= VM_IO;
  Vma->vm_flags |= VM_RESERVED;
  if (remap_pfn_range(vma, vma->vm_start,
              virt_to_phys(dev- >data)>> PAGE_SHIFT,
              size,
              vma->vm_page_prot))
     return -EAGAIN;
  return 0;
}

 

memdev.源码

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

/*mem设备描述结构体*/
struct mem_dev                                    
{                                                       
  char *data;                     
  unsigned long size;      
};

#endif /* _MEMDEV_H_ *

 

posted @ 2020-11-24 17:08  11YS  阅读(515)  评论(0)    收藏  举报