背景:
记录为什么,怎么解决的,前因后果流水账
记录内容来自阅读书籍,《嵌入式C语言自我修养:从芯片、编译器到操作系统》
章节5.5 mmap的映射区域探秘
程序执行
|
怎么执行?
|
可执行文件加载到内存
|
怎么加载?
|
|---------- 1.常规文件I/O操作。 read/write系统调用接口
|
|---------- 2.mmap系统调用。文件映射到进程虚拟空间,对映射区域读写
先说read、write:
打开文件路径---> 获得fd ---> 得到inode ---> 找到文件在磁盘的位置。然后读写位置。
===========
|
|
|
实际问题,读写磁盘老是要转动磁盘到某一位置(涉及硬件原理:硬盘的内部结构包括磁头、磁道、扇区、柱面,读写涉及磁头定位等问题)
|
|
如何解决 ?
|
|
linux系统提供磁盘缓冲机制
|
|
|------- 1. 提出分页的概念,用页来缓存磁盘上的普通文件和设备文件
2. 每次读写,先看页缓存中是否存在,不存在就把磁盘数据读取到页中
3. 页缓存超过阈值,或刷新时间。linux内核把数据写回磁盘。
上面说的是页缓存机制。
|
|
有何问题 ?
|
|
但是频繁read/wirte就会读来读取,内核和用户空间数据拷贝次数太多。
|
|
如何优化 ?
|
|
减少内核负担,在用户空间搞个I/O缓冲区,先把内核页缓存读到用户I/O缓存,用户频繁使用时,就直接查看用户的这块I/O空间。(fread/fwrite对read/write的封装)
|
|
有何问题 ?
|
|
减少了系统调用的次数,减少了内核的负担。但是!用户空间多个缓存区,这个增加了次数,负担转移而已,当数据量大,拷贝也的开销依旧存在。
|
|
如何进一步优化?
|
|
答案:用户空间文件直接映射到进程的虚拟空间
|
|
|----- 通过mmap, 文件内容和地址一一对应,对映射地址的读写相当于对磁盘文件的读写。
----------------
这个就是mmap由来的前因后果。
mmap函数:
/* addr: 映射虚拟内存的起始地址,一般为NULL
* length: 映射内存区域大小
* prot: 内存保护标志PROT_EXEC, PROT_READ, PROT_WRITE
* fd: 要映射的文件
* offset: 文件偏移
**/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
int i;
char *p_map;
fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
write(fd, "", 14);
p_map = (char*)mmap(NULL, 20, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(p_map == MAP_FAILED)
{
perror("mmap");
return -1;
}
close(fd);
if (fd == -1)
{
perror("close");
return -1;
}
memcpy(p_map, "hello world_world_world_world!\n", 14);
sleep(5);
if(munmap(p_map, 20) == -1)
{
perror("munmap");
return -1;
}
return 0;
}
运行: