内存映射
1.常用方法
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
成功,返回存储器起始地址;失败,返回MAP_FAILED
addr:指定映射存储区的起始地址,通常设置为0,表示由系统选择起始地址。
length:存储地址大小。
prot:映射存储区的保护要求。PROT_READ,存储器可读;PROT_WRITE,存储区可写;PROT_EXEC,存储区可执行;PROT_NONE,存储区不可访问。保护要求不能超过文件open模式访问权限。
flags:MAP_SHARED,表示对映射区的修改,相当于对文件的修改;MAP_PRIVATE,表示对映射区的修改,只是对文件副本的修改,原文件不变。一般设置flags为MAP_SHARED。
fd:映射存储区对应的文件。
offset:偏移量。
2.实例
1.创建两个进程,使用映射内存读写文件中的数据。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#define MEM_SIZE 1024
#define PATH "/tmp/out"
int main(int argc, char const *argv[])
{
void *ptr;
int fd;
pid_t pid;
struct stat stat_buf;
if(argc < 2){
fprintf(stderr, "argv error.\n");
exit(1);
}
if ((fd = open(PATH, O_CREAT | O_RDWR, 0660)) < 0)
{
perror("open()");
exit(1);
}
if(fstat(fd, &stat_buf) < 0){
perror("fstat()");
exit(1);
}
//被映射的文件如果大小小于映射存储区的大小,对存储区的修改超过了文件大小,会导致数据丢失。
//新创建的文件,大小为0,不对文件进行扩展,对相关存储区的第一次引用时会产生SIGBUS信号。
if(stat_buf.st_size < MEM_SIZE){
ftruncate(fd, MEM_SIZE);
}
ptr = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
perror("mmap()");
exit(1);
}
close(fd);
pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{ //child
memset(ptr, 0, MEM_SIZE);
strcpy(ptr, argv[1]);
}
else
{ //parent
wait(NULL);
puts(ptr);
}
munmap(ptr, MEM_SIZE);
exit(0);
}
falgs为MAP_SHARED,执行结果:
shbj@ubuntu:~/develop/workspace/c/hio$ ./mmap1 "Hello world"
Hello world
shbj@ubuntu:~/develop/workspace/c/hio$ cat /tmp/out
Hello worldshbj@ubuntu:~/develop/workspace/c/hio$ ./mmap1 "Good morning"
Good morning
shbj@ubuntu:~/develop/workspace/c/hio$ cat /tmp/out
Good morningshbj@ubuntu:~/develop/workspace/c/hio$
falgs为MAP_PRIVATE,执行结果:
shbj@ubuntu:~/develop/workspace/c/hio$ cat /tmp/out
Good morningshbj@ubuntu:~/develop/workspace/c/hio$ ./mmap1 "Hello world"
Good morning
shbj@ubuntu:~/develop/workspace/c/hio$ cat /tmp/out
Good morningshbj@ubuntu:~/develop/workspace/c/hio$
对比两组执行结果,可以看出flags参数为MAP_SHARED,对映射区的进行修改,对应的文件也修改了;MAP_PRIVATE,对映射区进行修改,映射区中的内容改变了,但是原文件中的内容没有改变。
3.注意
- 被映射的文件如果大小小于映射存储区的大小,对存储区的修改超过了文件大小,会导致数据丢失。
- 如果文件大小为0(如新建的文件),不对文件进行拓展,对相关存储区的第一次引用时会产生SIGBUS信号,导致进程终止。
- 所以使用内存映射时,要先调用fstat()函数获取到文件大小,再根据文件大小是否为0或小于存储区的大小,判断是否调用ftruncate()函数对文件进行扩展。