Yanyg - Software Engineer

MMAP机制分析

目录

#+PREVIEW_BEGIN 用strace跟踪常见命令,可以看到有大量 mmap 调用:

~$ strace ls 2>&1 | grep mmap
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1da2f60000
mmap(NULL, 152705, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1da2f3a000
mmap(NULL, 2259664, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1da2b18000
mmap(0x7f1da2d3c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x24000) = 0x7f1da2d3c000
mmap(0x7f1da2d3e000, 6864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1da2d3e000
mmap(NULL, 3795360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1da2779000
mmap(0x7f1da2b0e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7f1da2b0e000
mmap(0x7f1da2b14000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1da2b14000
...

通过 /proc/<pid>/maps 查看指定进程映射了哪几个文件:

~$ cat /proc/self/maps
556e0e237000-556e0e23f000 r-xp 00000000 08:02 786434                     /bin/cat
556e0e43e000-556e0e43f000 r--p 00007000 08:02 786434                     /bin/cat
556e0e43f000-556e0e440000 rw-p 00008000 08:02 786434                     /bin/cat
556e0fdb0000-556e0fdd1000 rw-p 00000000 00:00 0                          [heap]
7f7a531df000-7f7a5352e000 r--p 00000000 08:02 558542                     /usr/lib/locale/locale-archive
7f7a5352e000-7f7a536c3000 r-xp 00000000 08:02 9699336                    /lib/x86_64-linux-gnu/libc-2.24.so
7f7a536c3000-7f7a538c3000 ---p 00195000 08:02 9699336                    /lib/x86_64-linux-gnu/libc-2.24.so
7f7a538c3000-7f7a538c7000 r--p 00195000 08:02 9699336                    /lib/x86_64-linux-gnu/libc-2.24.so
7f7a538c7000-7f7a538c9000 rw-p 00199000 08:02 9699336                    /lib/x86_64-linux-gnu/libc-2.24.so
7f7a538c9000-7f7a538cd000 rw-p 00000000 00:00 0
7f7a538cd000-7f7a538f0000 r-xp 00000000 08:02 9699331                    /lib/x86_64-linux-gnu/ld-2.24.so
7f7a53ac5000-7f7a53ac7000 rw-p 00000000 00:00 0
7f7a53acb000-7f7a53af0000 rw-p 00000000 00:00 0
7f7a53af0000-7f7a53af1000 r--p 00023000 08:02 9699331                    /lib/x86_64-linux-gnu/ld-2.24.so
7f7a53af1000-7f7a53af2000 rw-p 00024000 08:02 9699331                    /lib/x86_64-linux-gnu/ld-2.24.so
7f7a53af2000-7f7a53af3000 rw-p 00000000 00:00 0
7ffc991b3000-7ffc991d4000 rw-p 00000000 00:00 0                          [stack]
7ffc991e8000-7ffc991ea000 r--p 00000000 00:00 0                          [vvar]
7ffc991ea000-7ffc991ec000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

#+PREVIEW_END

1 使用mmap读写文件

如下程序更新一个文件,让文件内容在字母A-Z之间变动:

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

int main(int argc, char *argv[])
{
        int fd;
        pid_t pid;
        struct stat sb;
        char *addr, val, cmdbuf[128];
        const char *pathname = "/tmp/mmap.test";

        if (argc > 1)
                pathname = argv[1];

        printf("pathname: %s\n", pathname);
        fd = open(pathname, O_RDWR|O_CREAT, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if (-1 == fd) {
                perror("open failed");
                return EXIT_FAILURE;
        }

        /* ensure up to 2MB */
        lseek(fd, 1024*1024*2, SEEK_CUR);
        write(fd, "", 1);
        if (-1 == fstat(fd, &sb)) {
                perror("fstat failed");
                return EXIT_FAILURE;
        }

        if (!S_ISREG(sb.st_mode)) {
                perror("Not a Regular file");
                return EXIT_FAILURE;
        }

        printf("file size:%lu\n", sb.st_size);
        addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (!addr) {
                perror("mmap failed");
        }

        val = addr[0];
        if ('A' > val || 'Z' <= val)
                val = 'A';
        val += 1;
        memset(addr, val, sb.st_size);

        pid = getpid();
        sprintf(cmdbuf, "ls -l /proc/%u/map_files/", pid);
        system(cmdbuf);
        printf("---------------------------\n");
        sprintf(cmdbuf, "cat /proc/%u/maps", pid);
        system(cmdbuf);

        return 0;
}

#+RESULTS:
total 0
pathname: /tmp/mmap.test
file size:2097153
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 555f1c428000-555f1c429000 -> /tmp/babel-M94bzk/C-bin-W4q33F
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 555f1c629000-555f1c62a000 -> /tmp/babel-M94bzk/C-bin-W4q33F
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 555f1c62a000-555f1c62b000 -> /tmp/babel-M94bzk/C-bin-W4q33F
lrw--–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f04be000-7ff4f06bf000 -> /tmp/mmap.test
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f06bf000-7ff4f0854000 -> /lib/x86_64-linux-gnu/libc-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0854000-7ff4f0a54000 -> /lib/x86_64-linux-gnu/libc-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0a54000-7ff4f0a58000 -> /lib/x86_64-linux-gnu/libc-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0a58000-7ff4f0a5a000 -> /lib/x86_64-linux-gnu/libc-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0a5e000-7ff4f0a81000 -> /lib/x86_64-linux-gnu/ld-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0c81000-7ff4f0c82000 -> /lib/x86_64-linux-gnu/ld-2.24.so
lr---–— 1 yanyg yanyg 64 Jan 26 15:20 7ff4f0c82000-7ff4f0c83000 -> /lib/x86_64-linux-gnu/ld-2.24.so
555f1c428000-555f1c429000 r-xp 00000000 08:02 11927613 /tmp/babel-M94bzk/C-bin-W4q33F
555f1c629000-555f1c62a000 r–p 00001000 08:02 11927613 /tmp/babel-M94bzk/C-bin-W4q33F
555f1c62a000-555f1c62b000 rw-p 00002000 08:02 11927613 /tmp/babel-M94bzk/C-bin-W4q33F
555f1e396000-555f1e3b8000 rw-p 00000000 00:00 0 [heap]
7ff4f04be000-7ff4f06bf000 rw-s 00000000 08:02 11796543 /tmp/mmap.test
7ff4f06bf000-7ff4f0854000 r-xp 00000000 08:02 9699336 /lib/x86_64-linux-gnu/libc-2.24.so
7ff4f0854000-7ff4f0a54000 —p 00195000 08:02 9699336 /lib/x86_64-linux-gnu/libc-2.24.so
7ff4f0a54000-7ff4f0a58000 r–p 00195000 08:02 9699336 /lib/x86_64-linux-gnu/libc-2.24.so
7ff4f0a58000-7ff4f0a5a000 rw-p 00199000 08:02 9699336 /lib/x86_64-linux-gnu/libc-2.24.so
7ff4f0a5a000-7ff4f0a5e000 rw-p 00000000 00:00 0
7ff4f0a5e000-7ff4f0a81000 r-xp 00000000 08:02 9699331 /lib/x86_64-linux-gnu/ld-2.24.so
7ff4f0c56000-7ff4f0c58000 rw-p 00000000 00:00 0
7ff4f0c7e000-7ff4f0c81000 rw-p 00000000 00:00 0
7ff4f0c81000-7ff4f0c82000 r–p 00023000 08:02 9699331 /lib/x86_64-linux-gnu/ld-2.24.so
7ff4f0c82000-7ff4f0c83000 rw-p 00024000 08:02 9699331 /lib/x86_64-linux-gnu/ld-2.24.so
7ff4f0c83000-7ff4f0c84000 rw-p 00000000 00:00 0
7ffd5f9f2000-7ffd5fa13000 rw-p 00000000 00:00 0 [stack]
7ffd5fadc000-7ffd5fade000 r–p 00000000 00:00 0 [vvar]
7ffd5fade000-7ffd5fae0000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

2 mmap的内核实现