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;
}