mmap系统调用如下:
void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
addr:指定映射的起始地址,通常设置为NULL,由系统指定;
length:映射到内存的文件长度;
prot:映射区的保护方式,可以是PROT_EXEC,PROT_READ,PROT_WRITE;
flags:映射区的特性,可以是MAP_SHARED,MAP_PRIVATE;
fd:由open返回的文件描述符,表示要映射的文件;
offset:文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射;
内存映射函数mmap:负责把文件内容映射到进程的虚拟地址空间中,映射之后,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等函数。
(文件在open之后直接mmap映射,然后用指针操作即可)
munmap系统调用如下:
int munmap(void *start,size_t length);
取消参数start所指向的映射内存,参数length表示欲取消映射的内存的大小;
(close之前要先调用munmap解除映射)
代码实例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
int main(int argc, char** argv)
{
int fd = 0;
char *start =NULL;
charbuffer[100] = {0};
fd =open("testfile", O_RDWR);
start =mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(buffer,start);
printf("buffer = %s\n", buffer);
strcpy(start,"now buffer is not null!");
munmap(start,100);
close(fd);
return 0;
}
执行结果:
(mmap系统调用不会影响原来文件的长度)
虚拟内存区域
虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。
一个进程的内存映像由下面几部分组成:程序代码,数据,BSS,栈区域,以及内存映射的区域。
(每个进程都有自己独立的虚拟地址空间)
进程的虚拟内存区域可以通过/proc/pid/maps查看:
每一行的域为:
start-end perm offset major:minor inode
start:该区域的起始虚拟地址;
end:该区域的结束虚拟地址;
perm:读写和执行权限,表示对这个区域,允许进程做什么,这个域的最后一个字符要么是p表示私有的,要么是s表示共享的;
offset:被映射部分在文件中的起始地址;
major、minor:主次设备号;
inode:索引节点;
Linux内核中,使用结构vm_area_struct(<linux/mm_types.h>)来描述虚拟内存区域:
struct vm_area_struct
{
struct mm_struct * vm_mm;/* The address space we belong to. */
unsigned long vm_start;/* Our start address within vm_mm. */
unsigned long vm_end;/* The first byte after our end address within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next;
pgprot_t vm_page_prot;/* Access permissions of this VMA. */
unsigned long vm_flags;/* Flags, see mm.h. */
...
};
映射一个设备是指把用户空间的一段地址关联到设备内存(设备的物理地址)上,用户空间的地址会由系统提供,设备内存的地址通过查询可以获得,因此我们只需要关心“物理地址与虚拟地址如何关联”?
建立虚拟地址到物理地址的页表。
那么又是“如何建立页表”?
方法有两种:
1、 使用remap_pfn_range一次建立所有页表;
2、 使用nopageVMA方法每次建立一个页表;
int remap_pfn_range(struct vm_area_struct *vma, unsigned longaddr,
unsigned long pfn, unsigned long size,pgprot_t prot)
remap_pfn_range - remap kernel memory to userspace;
vma: user vma to map to,虚拟内存区域指针;
addr: target user address to start at,虚拟地址的起始值;
pfn: physical address of kernel memory,要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到;
size: size of map area,要映射的区域的大小;
prot: page protection flags for this mapping,VMA的保护属性;