1、mmap函数原型

void *mmap(void *addr, size_t len, int prot, int flag, int fd, offset_t offset)
作用:
(1)内存映射函数mmap,负责把文件内容映射到进程的虚拟地址空间。这样做的目的就是减少read和write操作。
(2)也可以通过匿名映射实现进程通信。
addr:指定映射起始地址,通常为NULL,一般由内核指定
length:映射的内存文件长度
prot:映射区的保护方式:
PROT_EXEC:映射区可被执行
PROT_READ:映射区可被读取
PROT_WRITE:映射区可被读写
flags:映射区的特性
MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射 该文件的进程分享
MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write技术),对此区域的修改不会写回文件。
fd:文件
offset:文件的偏移,一般是0,从文件开头映射。

匿名映射:匿名内存映射适用于具有亲属关系的进程之间;由于父子进程之间的这种特殊的父子关系,在父进程中先调用mmap(),然后调用fork(),那么,在调用fork() 之后,子进程继承了父进程的所有资源,当然也包括匿名映射后的地址空间和mmap()返回的地址,这样父子进程就可以通过映射区域进行通信了;

 

 

2、int munmap(void *start, size_t length)
作用:解除映射。start地址是mmap的返回值。

3、虚拟内存区域
cat /proc/pid/maps可以查看所有的vma

linux内核通过vm_area_struct结构体来记录一个vma的数据:
主要包括几个成员:
vm_start,vm_end,vm_flags.等等

 

4、mmap实例

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

int main(void)
{
    int fd;
    char *start;
    
    fd=open("testfile",O_RDWR);
    start=mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0);
    
    strcpy(buf, start);//读数据
    printf("buf=%s\n", buf);
    
    strcpy(start,"buf is not null!" );//写数据
    munmap(start, 100);
    close(fd);
    return 0;
}

5、匿名映射实例

(20条消息) mmap匿名映射_SweeNeil的博客-CSDN博客_mmap匿名映射

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <semaphore.h>
 
struct file_content
{
    sem_t st;
    void *pv;
}file_content;
 
#define NINT 16
 
int main (int argc, char *argv[])
{
    struct file_content *pfc;
    pfc =mmap (NULL, sizeof (file_content) + sizeof (int) * NINT, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
    if (pfc == MAP_FAILED)
    {
        printf ("map failed!\n");
        exit(3);
    }
 
    //信号量初始化为1,用于互斥
    sem_init (&(pfc->st), 1, 1);        
    //pv指向结构体下一个字节的地址
    pfc->pv = (void *) ((char *) pfc + sizeof (struct file_content));     
    printf("结构体地址:\t\t%x\n结构体下一位置地址:\t\t%x\n", (int)pfc, (int)pfc->pv);
 
    //不加上这句的话,可能输出会卡在那里!
    setbuf(stdout,NULL);//和fflush函数没什么区别吧
 
    //子进程
    if (fork() == 0)        
    {
        int i;
        while (1)
        {
            sem_wait (&(pfc->st));
            printf ("Child process\n");
            int *p = pfc->pv;
            for (i = 0; i < NINT; i++)
            {
                p[i] = 2 * i;
            }   
            for (i = 0; i < NINT; i++)
            {
                printf ("%d ", p[i]);
            }
            printf ("\n"); 
            sem_post (&(pfc->st)); 
            sleep(2); 
        }
    }
    else// 父进程
    {
        int i;
        while (1)
        {
            sem_wait (&(pfc->st));
            printf ("Father process\n");
            int *p = pfc->pv;

            for (i = 0; i < NINT; i++)
            {
                printf ("%d ", p[i]);
            }
            printf ("\n");
            sem_post (&(pfc->st));
            sleep(2);
        }
    }
 
    if (munmap (pfc, sizeof (file_content) + sizeof (int) * NINT) == -1)
    {
          printf ("ummap!\n");
          exit (2);
    }
    return 0;
}

 

6、mmap设备操作
映射一个设备是指用户空间的一段地址关联到设备内存上。然后直接通过用户空间内存对设备内存进行访问。
mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表。

如何建立页表?建立什么页表?
(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:虚拟内存区域指针
pfn: 要映射的物理地址所在的物理页帧号,可将物理地址>>PAG_SHIFT得到。物理地址如何得到呢???
addr:虚拟地址的起始值
size:要映射的区域大小
prot:VMA的保护属性

实例:
int memdev_mmap(struct file *filep, struct vma_area_struct *vma)//参数是如何获取到的??
{
  vma->vm_flags |= VM_IO;
  vma->vm_flags |= VM_RESERVED;

  remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT, size, vma->vm_page_prot);
  return 0;
}