Linux C mmap函数学习

Linux C mmap函数学习

在写操作系统共享内存实验的时候,用到了Linux C中的mmap函数,觉得比较有意思,在此记录一下。

mmap实现共享内存原理

mmap函数的作用是将磁盘上文件的一块区域映射到内存中,使得进程可以直接访问内存来获取和修改文件内容,而无须通过readwrite。其具体的加载过程使用了lazy loading策略,在建立的时候并没有直接将内容拷贝到内存,甚至都没有建立虚拟内存到物理内存的映射,而是在访问的时候使用缺页异常来载入,可以在下面链接的博文中看到详细内容。

而要使用mmap实现共享内存,从我的理解看,其原理是对同一个文件,不同的进程建立映射后,映射区在物理内存中的地址是相同的,尽管在每个进程看到的虚拟内存中的地址是不一样的。因为虚拟内存是按页分配的,所以mmap要求映射区的大小是页大小的整数倍(这些都是个人理解)。

Linux环境进程间通信(五)共享内存(上)

3、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

5、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。

mmap应用

以下是使用mmap实现文件复制的代码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void mmapcopy(int, int, size_t);

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("Usage: %s SOURCE DEST\n", argv[0]);
        return 0;
    }

    int src_fd, dst_fd;
    struct stat stat;

    if ((src_fd = open(argv[1], O_RDONLY)) == -1) {
        perror("open src");
        return -1;
    }
    fstat(src_fd, &stat);

    if ((dst_fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0664)) == -1) {
        perror("open dst");
        return -1;
    }

    ftruncate(dst_fd, stat.st_size); // 如果没有该语句会报Bus error
    mmapcopy(src_fd, dst_fd, stat.st_size);

    close(src_fd);
    close(dst_fd);

    return 0;
}

void mmapcopy(int src_fd, int dst_fd, size_t len)
{
    void *src, *dst;
    int rt;

    if ((src = mmap(0, len, PROT_READ, MAP_PRIVATE, src_fd, 0)) == MAP_FAILED) {
        printf("src mmap error: %s\n", strerror(errno));
        exit(-1);
    }

    if ((dst = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, dst_fd, 0)) == MAP_FAILED) {
        printf("src mmap error: %s\n", strerror(errno));
        exit(-1);
    }

    memcpy(dst, src, len); // 如果dst和src相反会出现Segmentation fault(因为src是MAP_PRIVATE)

    if (munmap(src, len) == -1) {
        perror("munmap src");
        exit(-1);
    }
    if (munmap(dst, len) == -1) {
        perror("munmap dst");
        exit(-1);
    }
}

Interesting

有趣的是malloc函数在分配大于128KB的内存空间时使用的是mmap

0x951d008   // 1KB
0x951d818   // 128KB
0xb7530008  // 256KB

打印一下malloc分配不同大小内存返回的地址,可以看到大于128KB的内存地址和前两个都很不一样,因为mmap分配的内存位于堆和栈空间之间。

除此之外,动态链接库的共享也是用mmap实现的。

linux下共享内存mmap()方法和shmget()方法的疑问?

当malloc()分配内存时,在其中会调用brk()或mmap()向系统申请1块内存,小于128KB的内存空间,实际通过brk()方法申请;大于128KB的内存空间,实际通过mmap()方法申请。

mmap()相当于在页表中注册这块内存的虚地址,使该虚地址空间有效。在首次访问该虚地址空间时会触发缺页异常。

在linux系统的缺页异常中,会测试该内存块的虚地址是否合法,若为合法的虚地址空间,则说明是新分配的内存块尚未被映射到物理地址空间,则在缺页异常中完成地址映射;若给出的内存块虚地址不存在,则说明本次访问非法,系统抛出Segmentation fault错误。

你可能感兴趣的:(c)