介绍
mmap并不是什么新技术,早在4.2BSD的系统说明中就已经出现了mmap相关函数,并且mmap属于POSIX标准。在一众开源软件中都使用到了mmap,如Etcd、InfluxDB、RocketMQ等。
想要理解MMAP,就不得不说到** 虚拟内存(Virtual Memory,VM)**
虚拟内存 VM
虚拟内存是现代操作系统重要的一部分,它具备3个重要的能力:
- 把内存用做硬盘的高速缓存快速交换数据
- 为每个进程提供一致的地址空间,简化内存管理
- 保护每个进程的地址空间不被其它进程破坏
为了让每个进程都有一致的地址空间,引入虚拟内存空间的概念,由虚拟内存映射至物理内存,进程就无需关心数据存放在物理内存哪个位置。CPU通过虚拟地址访问内存,由CPU的**内存管理单元(Memory Management Unit,MMU)**负责将虚拟地址翻译为物理地址。
虚拟内存按一定大小分割为页——虚拟页(Virtual Page,VP)。n位虚拟地址分为两部分,p位虚拟地址偏移量和(n-p)位的虚拟页号(Virtual Page Number,VPN),p由虚拟页大小决定。为了将虚拟地址翻译为物理地址,每个进程都有独立的 页表(Page Table,PT),由 页表条目(Page Table Entry,PTE) 组成的数组,每条PTE记录了虚拟页号 ->物理页号(Physical Page Number,PPN)/硬盘地址 的映射关系,就好像字典的目录一样。MMU翻译时会读取页表,由操作系统负责维护页表内容。
mmap() 就是将虚拟地址映射至硬盘上的对象,当CPU首次请求这部分虚拟地址时,发现有效位为0即缺页,触发 页面调度(paging),将页从磁盘换入内存。如果页被修改则变为脏页,操作系统控制将脏页写回硬盘。
Golang实现mmap调用
Golang使用系统调用的时候,需要注意 syscall 已经被弃用,替代的库为 golang.org/x/sys
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package main
import (
"bytes"
"fmt"
"os"
"strings"
"golang.org/x/sys/unix"
)
const FILENAME = "test.mmap"
func main() {
pagesize := os.Getpagesize()
file, _ := os.OpenFile(FILENAME, os.O_RDWR|os.O_CREATE, 0644)
state, _ := file.Stat()
if state.Size() == 0 {
_, _ = file.WriteAt(bytes.Repeat([]byte{'0'}, pagesize), 0)
state, _ = file.Stat()
}
fmt.Printf("pagesize: %d\n filesize: %d\n", pagesize, state.Size())
data, _ := unix.Mmap(int(file.Fd()), 0, int(state.Size()), unix.PROT_WRITE, unix.MAP_SHARED)
// 关闭文件,不影响mmap
file.Close()
//for i := 0; i < 8; i++ {
//data[i] = '1'
//}
for i, x := range strings.Split("hello world", "") {
data[i] = []byte(x)[0] // string 转 byte ,用[]byte(*)[index]的方式
//fmt.Println(i, x)
}
unix.Munmap(data)
}
|
参考
文章作者
lixueping
上次更新
2020-02-23
(883c958)
许可协议
MIT