Linux系统调用mmap / munmap

prototype

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

mmap

  1. 在调用此函数的进程的虚拟内存空间中,创建一个内存映射区域。
  2. addr指定内存映射的起始位置,如果是NULL则由内核负责分配(page大小对齐)。
    使用NULL可移植性好。
  3. addr不为NULL时,kernel把这个值作为建议值(hint)。
    Linux上会选择page边间附近的空间创建映射(总是会大于或等于/proc/sys/vm/mmap_min_addr中指定的值)。
  4. 如果addr指定的位置已经存在映射,则kernel会选择一个新的地址(不一定和addr有关)。
  5. 返回值为创建的内存映射的地址。
  6. 文件内存映射的内容(file mapping, 和anonymous mapping相反),使用fd指定的对象内容进行初始化,内容从offset开始(pagesize整数倍,sysconf(_SC_PAGE_SIZE)),长度为length。
  7. mmap已返回,fd就可以关闭。
  8. prot(protection)指定内存映射的保护模式(不能和file的open mode冲突)。
    可选值为prot_none或者其他值进行or。
    PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE
  9. flags参数:
    1)这个参数影响内存映射内容的修改,是否对其他进程(映射了相同内存区域)或者被映射的文件是可见的。
    2)
    MAP_SHARED: 内存映射的修改对其他进程和底层文件都可见,会忽略unknown flags。
    MAP_SHARED_VALIDATE (since Linux 4.15, Linux extension),和MAP_SHARED相同,除了遇到unknown flags会报错。和MAP_SYNC配合使用,避免系统崩溃时数据丢失。
    MAP_PRIVATE:内存映射的修改对其他进程和底层文件都不可见。
    MAP_ANONYMOUS:不用映射一个文件,内存区域初始化为0。fd可忽略,为了兼容性设置为-1(某些系统要求)。
    MAP_FIXE:强制要求map到addr指定的地址(addr必须是page对齐)。由于不同版本kernel、libc等内存布局不同,慎用此选项!
    MAP_NORESERVE:不为内存映射保留swap空间。当物理内存耗尽时,对内存映射的写操作会遇到SIGSEGV错误。

unmap

  1. 删除addr指定的内存映射,对内存映射范围内的地址引用都将失效。
  2. 进程结束,内存映射会自动unmap。
  3. addr必须是page size对齐的,length无此要求。
  4. 返回值:success - 0, failed - -1(errno)。

Demo

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

#define handle_error(msg) \
    do {
      
       perror(msg); exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
    
    
    char *addr;
    int fd;
    struct stat sb;
    off_t offset, pa_offset;
    size_t length;
    ssize_t s;

    if (argc < 3 || argc > 4) {
    
    
        fprintf(stderr, "%s file offset [length]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        handle_error("open");

    if (fstat(fd, &sb) == -1)           /* To obtain file size */
        handle_error("fstat");

    offset = atoi(argv[2]);
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
        /* offset for mmap() must be page aligned */

    if (offset >= sb.st_size) {
    
    
        fprintf(stderr, "offset is past end of file\n");
        exit(EXIT_FAILURE);
    }

    if (argc == 4) {
    
    
        length = atoi(argv[3]);
        if (offset + length > sb.st_size)
            length = sb.st_size - offset;
                /* Can't display bytes past end of file */

    } else {
    
        /* No length arg ==> display to end of file */
        length = sb.st_size - offset;
    }

    addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
                MAP_PRIVATE, fd, pa_offset);
    if (addr == MAP_FAILED)
        handle_error("mmap");

    s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
    if (s != length) {
    
    
        if (s == -1)
            handle_error("write");

        fprintf(stderr, "partial write");
        exit(EXIT_FAILURE);
    }

    munmap(addr, length + offset - pa_offset);
    close(fd);

    exit(EXIT_SUCCESS);
}

猜你喜欢

转载自blog.csdn.net/yinminsumeng/article/details/130886065