使用mmap函数实现用户级存储器映射
每个进程都有自己的虚拟地址空间,我们知道除了堆中的虚拟内存我们可以由程序员灵活分配和释放,其他的区域的虚拟内存都有系统控制,那么还有没有其他方法让程序员去灵活控制虚拟内存呢?linux下的mmap函数就此而来,mmap函数可以为我们在进程的虚拟空间开辟一块新的虚拟内存,你可以将一个文件映射到这块新的虚拟内存,所以操作新的虚拟内存就是操作这个文件,下面我将介绍mmap函数的运用。
首先介绍mmap函数的头文件以及函数声明:
#include <sys/mman.h>
void *mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
start表示新的虚拟内存从这个地址开始,一般来说取NULL,那么将有内核来分配
length表示新的虚拟内存的大小
prot表示这块新的虚拟内存的访问权限:
PROT_EXEC:可执行
PROT_READ:可读
PROT_WRITE:可写
PROT_NONE:无法访问
flags表示 The flags argument determines whether updates to the mapping are visible to other processes mapping the same region, and whether updates are carried through to the underlying file. This behavior is determined by including exactly one of the following values in flags:
MAP_SHARED Share this mapping. Updates to the mapping are visible to other processes
that map this file, and are carried through to the underlying file. The
file may not actually be updated until msync(2) or munmap() is called.
MAP_PRIVATE
Create a private copy-on-write mapping. Updates to the mapping are not vis‐
ible to other processes mapping the same file, and are not carried through
to the underlying file. It is unspecified whether changes made to the file
after the mmap() call are visible in the mapped region.
fd:被映射文件的文件描述符
offset:从文件的offset处开始映射
example:
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { char *addr; int fd; struct stat sb; off_t offset, pa_offset; size_t length; ssize_t s; if (argc < 3 || argc > 4) { fprintf(stderr, "%s file offset [length]\n", argv[0]); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDONLY); if (fd == -1) handle_error("open"); if (fstat(fd, &sb) == -1) /* To obtain file size */ handle_error("fstat"); offset = atoi(argv[2]); pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); /* offset for mmap() must be page aligned */ if (offset >= sb.st_size) { fprintf(stderr, "offset is past end of file\n"); exit(EXIT_FAILURE); } if (argc == 4) { length = atoi(argv[3]); if (offset + length > sb.st_size) length = sb.st_size - offset; /* Can't display bytes past end of file */ } else { /* No length arg ==> display to end of file */ length = sb.st_size - offset; } addr = mmap(NULL, length + offset - pa_offset, PROT_READ, MAP_PRIVATE, fd, pa_offset); printf("addr is 0x%08x\n\n\n",&addr); if (addr == MAP_FAILED) handle_error("mmap"); s = write(STDOUT_FILENO, addr + offset - pa_offset, length); if (s != length) { if (s == -1) handle_error("write"); fprintf(stderr, "partial write"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } /* main */