linux系统编程——文件IO——文件映射

1. 什么是文件映射

将文件加载到页缓存,并将页缓存映射到用户虚拟空间,让应用程序直接访问页缓存。

2. api

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
       int munmap(void *addr, size_t length);

@addr : 多传NULL,给内核提建议
@length :
@prot : 内存保护方式,PROT_READ PROT_WRITE PROT_EXEC
@flags : 映射类型
MAP_FIXED : 要求将addr作为必要条件,若无法映射则返回错误。
MAP_PRIVATE : 不与其他进程共享映射,采用 copy-on-write 操作方式。因此此进程在内存上的修改不会反映在实际文件或其他进程映射中
MAP_SHARED : 与其他进程共享此映射,写映射等效于写文件。
你必须指定 MAP_SHARED 或 MAP_PRIVATED

3. 页对齐


页缓存的基本单位是页大小,所以 映射的起始地址必须在页大小的整数倍上(addr + offset)。
且映射的页面大小为页的整数倍,所以len若不足一个页面,则额外字节补0,且写额外字节不会影响文件。只有len个字节会被写回到文件。

4. mmap的优缺点

相对于 read write 有优点:

  • 读写时,避免read write在应用空间的副本
  • 写页面不会陷入内核,所以无上下文切换和系统调用开销
  • 能实现多进程共享
  • 查找映射文件,只需要指针操作,避免lseek

缺点:

  • 内存映射是页面大小的整数倍,若小文件则浪费空间
  • 内存映射必须放入进程虚拟地址空间,若使用32bit空间,内存映射非常大时,会导致地址空间碎裂,从而很难找到连续的大型可用空间。在64bit上极少出现这种问题
  • 创建和维护内存映射以及内核内部相关数据结构是要付出代价的。此开销通常会和双重副本开销相抵,尤其是对大型及频繁被访问的文件而言。

总结:
映射大型文件(浪费的空间占整个映射相当小的比例)或文件映射的大小为页面的大小的整数倍(无浪费空间),则可以从mmap的优点获得好处

5. 如何获得页大小

5.1 sysconf

long sysconf(int name);

sysconf是获得页大小的POSIX方法。用于获得系统特有信息。
name为配置项的值,若name无效则返回-1。
获得页大小:

long page_size = sysconf(_SC_PAGESIZE);

5.2 getpagesize()

int getpagesize(void);

linux提供 getpagesize返回页大小

5.3 PAGE_SIZE

页大小被静态定义在 PAGE_SIZE宏(<asm/page.h>)

int page_size = PAGE_SIZE;

由于未来内核可能不会导出宏到用户空间,所以不应该使用宏,sysconf是最好方法。

6. 写回

页缓存被写后,成了脏页,那什么时候写回呢?
和普通的write写回情况一样:要么内存用完了,被释放。要么时间阈值到了。
若想立即写回需要 msync

int msync(void *addr, size_t len, int flags);

flags:
MS_ASYNC : 异步化指定写回操作。即调用msync后立即返回,但会等会进行写回操作
MS_SYNC : 同步化指定写回操作。即msync将不会返回,直到写回完成。
通常使用 MS_ASYNC即可

posted on 2021-08-23 23:29  开心种树  阅读(265)  评论(0编辑  收藏  举报