# 文件系统，第 6 部分：文件映射到内存中以及共享内存

> 原文：<https://github.com/angrave/SystemProgramming/wiki/File-System%2C-Part-6%3A-Memory-mapped-files-and-Shared-memory>

## 操作系统如何将我的进程和库加载到内存中？

通过将文件的内容映射到进程的地址空间。如果许多程序只需要对同一文件的读访问（例如/ bin / bash，C 库），则可以在多个进程之间共享相同的物理内存。

程序可以使用相同的机制将文件直接映射到内存中

## 如何将文件映射到内存？

将文件映射到内存的简单程序如下所示。需要注意的要点是：

- mmap 需要一个文件描述符，因此我们需要先打开文件
- mmap仅适用于可查找的文件描述符，即“真”文件，而不是管道或套接字
- 我们寻求达到所需的大小并写入一个字节以确保文件的长度足够（如果不这样做，您的程序在尝试访问文件时会收到SIGBUS）。ftruncate也会起作用。
- 完成后，我们调用 munmap 从内存中取消映射文件

此示例还显示预处理器常量“ **LINE** ”和“ **FILE** ”，其中包含当前正在编译的文件的当前行号和文件名。

```c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int fail(char *filename, int linenumber) { 
  fprintf(stderr, "%s:%d %s\n", filename, linenumber, strerror(errno)); 
  exit(1);
  return 0; /*Make compiler happy */
}
#define QUIT fail(__FILE__, __LINE__ )

int main() {
  // We want a file big enough to hold 10 integers 
  int size = sizeof(int) * 10;

  int fd = open("data", O_RDWR | O_CREAT | O_TRUNC, 0600); //6 = read+write for me!

  lseek(fd, size, SEEK_SET);
  write(fd, "A", 1);

  void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  printf("Mapped at %p\n", addr);
  if (addr == (void*) -1 ) QUIT;

  int *array = addr;
  array[0] = 0x12345678;
  array[1] = 0xdeadc0de;

  munmap(addr,size);
  return 0;

}
```

我们的二进制文件的内容可以使用 hexdump 命令查看：


```
$ hexdump data
0000000 78 56 34 12 de c0 ad de 00 00 00 00 00 00 00 00
0000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000020 00 00 00 00 00 00 00 00 41 
```

细心的读者可能会注意到，我们的整数是以最低有效字节格式编写的（因为这是CPU的字节序），并且我们分配的文件字节太多了！


`PROT_READ | PROT_WRITE`选项指定虚拟内存保护。可以将选项`PROT_EXEC`（此处未使用）设置为允许 CPU 在内存中执行指令（例如，如果映射可执行文件或库，这将非常有用）。

## 内存映射文件有什么好处

对于许多应用程序，主要优点是：
简化编码 - 文件数据立即可用。无需解析传入的数据并将其存储在新的内存结构中。
文件共享 - 在多个进程之间共享相同数据时，内存映射文件特别有效。

对于简单顺序处理的注意事项，内存映射文件不一定比`read` / fscanf 等标准的“基于流”的方法更快。

## 如何在父进程和子进程之间共享内存？

简单 - 使用没有文件的`mmap` - 只需指定 MAP_ANONYMOUS 和 MAP_SHARED 选项！

```c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h> /* mmap() is defined in this header */
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main() {

  int size = 100 * sizeof(int);  
  void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  printf("Mapped at %p\n", addr);

  int *shared = addr;
  pid_t mychild = fork();
  if (mychild > 0) {
    shared[0] = 10;
    shared[1] = 20;
  } else {
    sleep(1); // We will talk about synchronization later
    printf("%d\n", shared[1] + shared[0]);
  }

  munmap(addr,size);
  return 0;
}
```

## 我可以为 IPC 使用共享内存吗？

可以的！举个简单的例子，您可以只保留几个字节，并在希望子进程退出时更改共享内存中的值。共享匿名内存是一种非常有效的进程间通信形式，因为没有复制、系统调用或磁盘访问开销 - 这两个进程实际上共享主内存的同一物理帧。

另一方面，共享内存（如多线程）为数据争用创造了空间。共享可写内存的进程可能需要使用同步基元（如互斥锁）来防止这些情况发生。

[转到文件系统：第 7 部分](https://github.com/angrave/SystemProgramming/wiki/File-System,-Part-7:-Scalable-and-Reliable-Filesystems)