Linux-API:mmap&flock
Linux API: introduction to mmap(2) and flock(2)
mmap
mmap(2) 比较折磨人,它的接口如下:
1 | #include <sys/mman.h> |
mmap 有两种 mapping:
- file mapping
- anonymous mapping
对于 1,fd 在 mmap 后可以 close:
1 | After the mmap() call has returned, the file descriptor, fd, can |
mmap 的 prot 有下列的语义:
PROT_EXEC: Pages may be executed.PROT_READ: Pages may be read.PROT_WRITE: Pages may be written.PROT_NONE:Pages may not be accessed.
mmap 的逻辑和 os 的 page cache 强相关,上述语义可能作用在 page 上,必要的时候给你个 SIGSEGV
rwx 三个选项都理解,PROT_NONE 可能用于做 Guard Page: https://stackoverflow.com/questions/12916603/what-s-the-purpose-of-mmap-memory-protection-prot-none
同时,flags 也有许多选项,除了 MAP_NONBLOCK MAP_LOCKED 这些,需要留意 MAP_SHARED 和 MAP_PRIVATE , 使用可以见下表。

上述也会影响 fork 的时候父子进程的 mmap 行为(可以回到一下之前对 fork 的吐槽)
文件映射
mmap 的时候,文件会被按照 offset 和 length 映射到内存中,如图:

这种 mapping 可能是 on-demand 的,即 mmap 之后不把 page 加载,需要的时候再访问。
对于 MAP_PRIVATE, 它可以:
- 被用在
PROT_READ | PROT_EXEC, 运行程序,这样不会修改程序的数据 - 共享一个只读的
.text
而共享的文件则如下图所示:

这种 memory mapping IO 会有特点:文件和 kernel 的 buffer 是由 os 自动 manage 的,同时,内核和用户进程不再和 write 一样,先写用户 buffer, 再写 kernel buffer.
当需要强制 flush 的时候,可以走 msync(2) 来强制刷盘
mmap 的映射可能比文件本身还大,未来的内存可能需要 ftruncate 处理:

匿名映射
数据会被初始化为 0
1 | MAP_ANONYMOUS |
杂项
mprotect(2) 切换进程页的访问权限
mlock 会讲内存 Page 驻留在物理内存中,不会swap 出去
madvise(2)
用户改内核的代码和调度是不现实的,但是很多时候用户比内核自己清楚需要调度成啥样。
madvise(2) 相当于提供上述相关的信息,“建议”内核做相关的操作。详见:https://www.man7.org/linux/man-pages/man2/madvise.2.html
Usage
BoltDB 的读取使用了 mmap. 内存空间是小于磁盘的,而 bolt 没有自己写 BufferPool, 而是用了 mmap, 可以看看代码:
1 | // mmap memory maps a DB's data file. |
DB::mmap 具体走到 bolt_unix.go 下的 mmap, 这里用 madvise + MADV_RANDOM 来表达访问的模式,同时把这次数据存储到 db 上
dataref 防止 syscall.Mmap 的结果被回收,具体访问的数据被丢到 data 上。
munmap 是一个反向操作:
1 | // munmap unmaps a DB's data file from memory. |
flock
https://man7.org/linux/man-pages//man2/flock.2.html
flock(2) 源自 BSD,flock 可以指定 LOCK_SH 和 LOCK_EX, 表示共享/互斥,也可以 LOCK_UN 解锁
本身调用 flock 会阻塞进程,不希望阻塞可以带上 LOCK_UB flag
flock 本身和 fd 是绑定的,也就是说,dup 产生的 fd 和 fork 子进程 产生的 fd 是同一把锁,这意味着,对它们的 LOCK_UN 处理需要额外注意。

可以参考一下代码的 flock 和 funlock, 内容在 bolt_unix.go:
1 | func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error { |
这里的 funlock 比较朴素,flock 采取了 LOCK_NB 和一个 retry 尝试结合,超过 timeout 后,程序会自动退出。
flock 只能以文件为粒度上锁,需要更细的控制需要使用 fcntl 。