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;
}
原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/274917.html