当我们使用 mmap 函数时,操作系统会做什么工作?

时间:2021-01-24 12:23:04

标签: c operating-system systems-programming

这是一个使用 mmap 函数将给定文件映射到内存的示例。在这个例子中,我没有使用 fwritewrite 函数将内容写入磁盘文件(只是将内容打印到 stdout),但修改后的内存内容实际上反映在磁盘文件上。我猜操作系统会跟踪映射内存并在修改映射内存时写入磁盘。我想知道操作系统的详细信息。

example.c

#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]){

    if(argc < 2){
        printf("File path not mentioned\n");
        exit(0);
    }

    const char *filepath = argv[1];
    int fd = open(filepath, O_RDWR);
    if(fd < 0){
        printf("\n\"%s \" could not open\n",
               filepath);
        exit(1);
    }

    struct stat statbuf;
    int err = fstat(fd, &statbuf);
    if(err < 0){
        printf("\n\"%s \" could not open\n",
                       filepath);
        exit(2);
    }

    char *ptr = mmap(NULL,statbuf.st_size,
            PROT_READ|PROT_WRITE,MAP_SHARED,
            fd,0);
    if(ptr == MAP_FAILED){
        printf("Mapping Failed\n");
        return 1;
    }
    close(fd);

    ptr[statbuf.st_size - 2] = '@';
    ssize_t n = write(1,ptr,statbuf.st_size);
    if(n != statbuf.st_size){
        printf("Write failed\n");
    }
    while(1);
}

mytext.txt

hello world!

编译运行

gcc example.c && ./a.out mytext.txt

由于最后一行没有退出程序,我可以通过 cat mytext.txt

在另一个终端中看到修改后的内容
hello world@

2 个答案:

答案 0 :(得分:1)

<块引用>

我猜操作系统会跟踪映射内存并在映射内存被修改时写入磁盘。

是和否。当然,操作系统会保存映射到磁盘文件的内存记录,并确保对该内存的更改写入磁盘。但是,我不希望立即写入更改。

基于常识(我已经很久没有看到操作系统的这些特定内部结构了),可能会发生的情况是管理文件系统的软件会记录磁盘上的某些块映射到内存并且,一旦它们被更改,它们就是“脏的”,这意味着它们已经被修改,最终需要写回磁盘。

操作系统可能会在某些情况下将脏页写入磁盘。例如,当磁盘上的文件已经关闭(这也意味着内存页已被取消映射,因为将内存映射到文件包括保持文件打开)并且有卸载磁盘的请求时,脏页必须是写入磁盘。作为策略选择,它们也可能会定期写入磁盘。

但是,即使更改没有写入磁盘,这些更改也可能看起来在您的文件中!当您读取文件时,文件系统会确定磁盘上的哪些块包含您正在请求的文件部分。然后它看到那些块已经在内存中。因此,为了满足您的读取请求,它为您提供内存中的数据,而不是从磁盘读取数据。因此,对于作为用户的您来说,即使数据尚未在磁盘上,但更改的数据实际上似乎在文件中。

这种行为实际上不符合管理文件系统的一般设计。如果您打开一个文件并读取了 100 个字节,文件系统软件最初必须将正确的块归档到磁盘上,将其读入内存,然后将您请求的 100 个字节复制到您的进程中。该块可能是 512 字节或 4096 字节或其他一些用于操作磁盘的大小。文件系统将如何处理该块?在请求一个文件的 100 个字节后,程序很可能请求另一个 100 个字节。再次从磁盘读取它是一种耻辱。所以文件系统软件会保留它。这意味着它会保存一个数据库,其中包含当前内存中的哪些磁盘的哪些块。

接下来,假设其他程序读取同一个文件。显然,如果必要的块已经在内存中,我们将从内存中的块中提供该程序数据,而不是再次从磁盘读取它。因此文件系统共享所有这些块(取决于适当的权限)。如果一个程序更改了文件中的数据,它会转到内存中的块,因此即使尚未写入磁盘,其他程序也会立即看到它。

内存映射文件与这种在内存中缓存磁盘块的方案密切相关。进程的虚拟内存只是映射到文件系统软件保存文件块的物理内存,并且每个进程都具有相同的文件视图,无论它是使用内存映射还是读写操作。

(这是一般信息;特定的操作系统可能会有不同的表现。)

答案 1 :(得分:1)

您的代码似乎是在 Linux 上编译的。

我假设是。

mmap(2) 系统调用与 virtual address space 相关,如本 OS textbook 中所述,虚拟地址空间由处理器的 MMU 管理。 2021 年,MMU 将成为处理器芯片的一部分。

所以 mmap 要求 OS kernel 重新配置 MMU。 CPU mode 更改为内核模式,就像在每个 syscalls(2) 中一样,内核代码将重新配置 MMU 和一些内部内核数据,以不同方式处理未来的 page faults

另请参阅 kernelnewbiesOSDEV wiki、Advanced Linux ProgrammingLinux Assembly HOWTO 并研究您的 Linux kernel 和/或 {{3} }.

您正在使用 gcc example.c 进行编译。考虑编译 GNU libc gcc -Wall -Wextra -g example.c(以获得有用的警告和 with 调试信息),然后使用 DWARFGDBstrace(1) 来了解您的可执行文件。

附注。您可能希望将 ltrace(1)perror(3)errno(3)

一起使用
相关问题