Analysis and implementation of mmap system call principle in linux

From: http://blog.chinaunix.net/uid-26215986-id-3360380.html

1. The mmap system call (function)
      void* mmap ( void * addr , size_t len ​​, int prot , int flags , int fd , off_t offset )
      The memory mapping function mmap is responsible for mapping the file content to the virtual memory space of the process. This memory is read and modified to realize the reading and modification of the file, without calling read, write and other operations.

2. mmap system call (parameters)
      1) addr: specify the starting address of the mapping, usually set to NULL, specified by the system.
      2) length: The length of the file mapped to memory.
      3) prot: the protection method of the mapped area, which can be: PROT_EXEC: the
             mapped area can be executed
             PROT_READ: the mapped area can be read
             PROT_WRITE: the mapped area can be written

      4) flags: The characteristics of the mapped area, which can be:
            MAP_SHARED: The data written to the mapped area will be copied back to the file and shared by other processes that map the file.
            MAP_PRIVATE: A write operation to the mapped area will result in a copy-on-write of the mapped area, and changes made to this area will not be written back to the original file.

      5) fd: The file descriptor returned by open, representing the file to be mapped.
      6) offset: The offset at the beginning of the file, which must be an integer multiple of the page size, usually 0, indicating that the mapping starts from the file header.

3.
      Unmap int munmap(void *start, size_t length)
     function: cancel the mapped memory pointed to by the parameter start, and the parameter length indicates the size of the memory to be canceled.
    Return value: return 0 if the release is successful, otherwise return -1, and the cause of the error is stored in errno.

Example analysis
mmap system call

4. Virtual memory area
      The virtual memory area is a homogeneous interval in the virtual address space of a process, that is, a continuous address range with the same characteristics. A process's memory image consists of the following parts: program code, data, BSS
and stack areas, and memory-mapped areas.

 The memory area of ​​a process can be viewed by looking at: /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 0400000-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

       The field of each line is: start_end perm offset major:minor inode
       1) Start: the starting virtual address of this area
       2) End: the virtual address of the end of this area
       3) Perm: read, write and execute permissions; it means that the process is allowed to this area doing what. The last character of this field is either p for private or s for shared.
       4) Offset: the starting address of the mapped part in the file
       5) Major, minor: major and minor device numbers
       6) Inode: index node

5、vm_area_struct
      Linux内核使用结构vm_area_struct()来描述虚拟内存区域,其中几个主要成员如下:
     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标志内存区域不能被换出。

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

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

      mmap如何完成页表的建立?
     方法有二:
     1)使用remap_pfn_range一次建立所有页表;
     2)使用nopage VMA方法每次建立一个页表。

     构造页表的工作可由remap_pfn_range函数完成,原型如下:
     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;
}

7、mmap设备方法实例
       1)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_ */

       2)memdev.c源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include 
#include "memdev.h"
static int mem_major = MEMDEV_MAJOR;
module_param(mem_major, int, S_IRUGO);
struct mem_dev *mem_devp; /*设备结构体指针*/
struct cdev cdev;
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
    struct mem_dev *dev;
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);
    if (num >= MEMDEV_NR_DEVS) 
            return -ENODEV;
    dev = &mem_devp[num];
    
    /*将设备描述结构指针赋值给文件私有数据指针*/
    filp->private_data = dev;
    
    return 0; 
}
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
  return 0;
}
static int memdev_mmap(struct file*filp, struct vm_area_struct *vma)
{
      struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
      
      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, vma->vm_end - vma->vm_start, vma->vm_page_prot))
          return  -EAGAIN;
                
      return 0;
}
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
  .owner = THIS_MODULE,
  .open = mem_open,
  .release = mem_release,
  .mmap = memdev_mmap,
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
  int result;
  int i;
  dev_t devno = MKDEV(mem_major, 0);
  /* 静态申请设备号*/
  if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");
  else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
    mem_major = MAJOR(devno);
  }  
  
  if (result < 0)
    return result;
  /*初始化cdev结构*/
  cdev_init(&cdev, &mem_fops);
  cdev.owner = THIS_MODULE;
  cdev.ops = &mem_fops;
  
  /* 注册字符设备 */
  cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
   
  /* 为设备描述结构分配内存*/
  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
  if (!mem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(mem_devp, 0, sizeof(struct mem_dev));
  
  /*为设备分配内存*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);
  }
    
  return 0;
  fail_malloc: 
  unregister_chrdev_region(devno, 1);
  
  return result;
}
/*模块卸载函数*/
static void memdev_exit(void)
{
  cdev_del(&cdev);   /*注销设备*/
  kfree(mem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}
MODULE_AUTHOR("yinjiabin);
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
 
3)测试程序源码
#include 
#include
#include
#include
#include
#include
int main()
{
 int fd;
 char *start;
 //char buf[100];
 char *buf;
 
 /*打开文件*/
 fd = open("/dev/memdev0",O_RDWR);
        
 buf = (char *)malloc(100);
 memset(buf, 0, 100);
 start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
 
 /* 读出数据 */
 strcpy(buf,start);
 sleep (1);
 printf("buf 1 = %s\n",buf); 
 /* 写入数据 */
 strcpy(start,"Buf Is Not Null!");
 
 memset(buf, 0, 100);
 strcpy(buf,start);
 sleep (1);
 printf("buf 2 = %s\n",buf);
       
 munmap(start,100); /*解除映射*/
 free(buf);
 close(fd);  
 return 0; 
}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324364557&siteId=291194637