104. munmap - 取消内存映射 見出しへのリンク

1. 函数介绍 見出しへのリンク

munmap 是一个 Linux 系统调用,用于取消之前通过 mmap 建立的内存映射。它释放指定的内存映射区域,使该区域不再可访问,并可能释放相关的物理内存或文件资源。

2. 函数原型 見出しへのリンク

#include <sys/mman.h>

int munmap(void *addr, size_t length);

3. 功能 見出しへのリンク

取消从 addr 开始、长度为 length 字节的内存映射区域。这个操作会释放映射区域,使程序无法再通过该地址访问映射的内容。

4. 参数 見出しへのリンク

  • void *addr: 要取消映射的内存区域起始地址
    • 必须是之前 mmap 调用返回的地址
    • 必须是页面对齐的地址
  • size_t length: 要取消映射的内存区域长度(以字节为单位)
    • 可以是原始映射长度的一部分
    • 系统会将其向上舍入到页面边界

5. 返回值 見出しへのリンク

  • 成功时返回 0
  • 失败时返回 -1,并设置 errno

6. 常见 errno 错误码 見出しへのリンク

  • EINVAL: 参数无效(地址不合法或长度为 0)
  • ENOMEM: 指定的地址范围未映射
  • EAGAIN: 暂时无法完成操作(很少见)
  • EFAULT: 地址范围包含无效内存

7. 相似函数,或关联函数 見出しへのリンク

  • mmap(): 创建内存映射
  • mremap(): 重新映射内存区域
  • msync(): 同步内存映射与文件
  • mprotect(): 修改内存保护属性
  • shm_open(), shm_unlink(): POSIX 共享内存
  • brk(), sbrk(): 调整程序数据段大小

8. 示例代码 見出しへのリンク

示例1:基本使用 - 文件映射的取消 見出しへのリンク

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

int main() {
    const char *filename = "test_file.txt";
    const char *content = "This is test content for memory mapping.\nSecond line of content.\nThird line of content.";
    
    printf("=== munmap 基本使用演示 ===\n");
    
    // 创建测试文件
    int fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        exit(EXIT_FAILURE);
    }
    
    // 写入测试内容
    write(fd, content, strlen(content));
    
    // 获取文件大小
    struct stat st;
    if (fstat(fd, &st) == -1) {
        perror("获取文件状态失败");
        close(fd);
        unlink(filename);
        exit(EXIT_FAILURE);
    }
    
    printf("创建测试文件: %s (大小: %ld 字节)\n", filename, (long)st.st_size);
    
    // 创建内存映射
    void *mapped_addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, 
                            MAP_SHARED, fd, 0);
    if (mapped_addr == MAP_FAILED) {
        perror("mmap 失败");
        close(fd);
        unlink(filename);
        exit(EXIT_FAILURE);
    }
    
    printf("✓ 成功创建内存映射\n");
    printf("  映射地址: %p\n", mapped_addr);
    printf("  映射内容: %.*s\n", (int)st.st_size, (char*)mapped_addr);
    
    // 修改映射内容
    strcpy((char*)mapped_addr, "Modified content via mmap");
    printf("✓ 修改映射内容\n");
    printf("  新内容: %.*s\n", (int)st.st_size, (char*)mapped_addr);
    
    // 取消内存映射
    printf("\n取消内存映射...\n");
    if (munmap(mapped_addr, st.st_size) == -1) {
        perror("munmap 失败");
        close(fd);
        unlink(filename);
        exit(EXIT_FAILURE);
    }
    
    printf("✓ 成功取消内存映射\n");
    
    // 验证文件内容(通过重新打开文件)
    printf("\n验证文件内容:\n");
    lseek(fd, 0, SEEK_SET);
    char buffer[256];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("  文件内容: %s", buffer);
    }
    
    // 清理资源
    close(fd);
    unlink(filename);
    
    printf("\n✓ 完成 munmap 演示\n");
    
    return 0;
}

示例2:部分映射区域的取消 見出しへのリンク

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

void demonstrate_partial_unmap() {
    printf("=== 部分映射区域取消演示 ===\n");
    
    // 创建大文件用于测试
    const char *filename = "large_test_file.txt";
    int fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        return;
    }
    
    // 创建 1MB 的文件
    size_t file_size = 1024 * 1024;  // 1MB
    if (ftruncate(fd, file_size) == -1) {
        perror("设置文件大小失败");
        close(fd);
        unlink(filename);
        return;
    }
    
    printf("创建 %zu 字节的测试文件\n", file_size);
    
    // 创建内存映射
    void *mapped_addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
                            MAP_SHARED, fd, 0);
    if (mapped_addr == MAP_FAILED) {
        perror("mmap 失败");
        close(fd);
        unlink(filename);
        return;
    }
    
    printf("✓ 创建内存映射: %p (大小: %zu)\n", mapped_addr, file_size);
    
    // 初始化部分数据
    size_t page_size = getpagesize();
    printf("系统页面大小: %zu 字节\n", page_size);
    
    // 在前几个页面写入数据
    for (int i = 0; i < 5; i++) {
        char data[64];
        snprintf(data, sizeof(data), "Page %d data", i);
        strcpy((char*)mapped_addr + i * page_size, data);
    }
    
    printf("✓ 在前5个页面写入测试数据\n");
    
    // 显示部分数据
    printf("映射区域前 5 个页面的内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("  页面 %d: %s\n", i, (char*)mapped_addr + i * page_size);
    }
    
    // 取消前两个页面的映射
    printf("\n取消前两个页面的映射...\n");
    if (munmap(mapped_addr, 2 * page_size) == -1) {
        perror("取消部分映射失败");
    } else {
        printf("✓ 成功取消前 %zu 字节的映射\n", 2 * page_size);
    }
    
    // 尝试访问已取消映射的区域(应该失败)
    printf("尝试访问已取消映射的区域:\n");
    if (munmap(mapped_addr, page_size) == -1) {
        if (errno == EINVAL) {
            printf("✓ 访问已取消映射区域失败 (EINVAL)\n");
        } else {
            printf("  失败: %s\n", strerror(errno));
        }
    }
    
    // 取消剩余映射区域
    printf("\n取消剩余映射区域...\n");
    void *remaining_addr = (char*)mapped_addr + 2 * page_size;
    size_t remaining_size = file_size - 2 * page_size;
    
    if (munmap(remaining_addr, remaining_size) == -1) {
        perror("取消剩余映射失败");
    } else {
        printf("✓ 成功取消剩余映射区域\n");
    }
    
    // 清理资源
    close(fd);
    unlink(filename);
}

void test_unmap_errors() {
    printf("\n=== munmap 错误处理测试 ===\n");
    
    // 测试无效地址
    printf("测试无效地址:\n");
    if (munmap((void*)0x1, 4096) == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        if (errno == EINVAL) {
            printf("    原因: 无效的地址\n");
        }
    }
    
    // 测试 NULL 地址
    printf("测试 NULL 地址:\n");
    if (munmap(NULL, 4096) == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        if (errno == EINVAL) {
            printf("    原因: NULL 地址无效\n");
        }
    }
    
    // 测试零长度
    printf("测试零长度:\n");
    void *dummy_addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (dummy_addr != MAP_FAILED) {
        if (munmap(dummy_addr, 0) == -1) {
            printf("  结果: 失败 - %s\n", strerror(errno));
            if (errno == EINVAL) {
                printf("    原因: 长度为 0\n");
            }
        }
        // 清理
        munmap(dummy_addr, 4096);
    }
    
    // 测试未映射的区域
    printf("测试未映射的区域:\n");
    if (munmap((void*)0x10000000, 4096) == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        if (errno == ENOMEM) {
            printf("    原因: 指定区域未映射\n");
        }
    }
}

int main() {
    printf("=== munmap 错误处理和部分取消演示 ===\n");
    
    demonstrate_partial_unmap();
    test_unmap_errors();
    
    return 0;
}

示例3:匿名映射和共享内存的管理 見出しへのリンク

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>

void demonstrate_anonymous_mapping() {
    printf("=== 匿名内存映射管理演示 ===\n");
    
    size_t page_size = getpagesize();
    size_t map_size = 4 * page_size;  // 4 个页面
    
    // 创建匿名内存映射
    void *anon_map = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (anon_map == MAP_FAILED) {
        perror("创建匿名映射失败");
        return;
    }
    
    printf("✓ 创建匿名映射: %p (大小: %zu)\n", anon_map, map_size);
    
    // 初始化数据
    for (int i = 0; i < 4; i++) {
        char data[64];
        snprintf(data, sizeof(data), "Anonymous page %d", i);
        strcpy((char*)anon_map + i * page_size, data);
    }
    
    printf("✓ 初始化匿名映射数据\n");
    
    // 显示数据
    printf("匿名映射内容:\n");
    for (int i = 0; i < 4; i++) {
        printf("  页面 %d: %s\n", i, (char*)anon_map + i * page_size);
    }
    
    // 取消映射
    printf("\n取消匿名映射...\n");
    if (munmap(anon_map, map_size) == -1) {
        perror("取消匿名映射失败");
    } else {
        printf("✓ 成功取消匿名映射\n");
    }
    
    // 尝试再次取消同一映射(应该失败)
    printf("再次取消同一映射:\n");
    if (munmap(anon_map, map_size) == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        if (errno == ENOMEM) {
            printf("    原因: 映射区域已不存在\n");
        }
    }
}

void demonstrate_shared_memory_unmap() {
    printf("\n=== 共享内存映射取消演示 ===\n");
    
    size_t page_size = getpagesize();
    size_t map_size = 2 * page_size;
    
    // 创建共享匿名映射
    void *shared_map = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                           MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared_map == MAP_FAILED) {
        perror("创建共享映射失败");
        return;
    }
    
    printf("✓ 创建共享映射: %p (大小: %zu)\n", shared_map, map_size);
    
    // 创建子进程测试共享内存
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork 失败");
        munmap(shared_map, map_size);
        return;
    }
    
    if (pid == 0) {
        // 子进程
        printf("子进程 (PID: %d):\n", getpid());
        printf("  访问共享映射: %p\n", shared_map);
        
        // 写入数据
        strcpy((char*)shared_map, "Data from child process");
        printf("  ✓ 写入共享数据\n");
        
        // 等待父进程处理
        sleep(1);
        
        // 取消自己的映射
        if (munmap(shared_map, map_size) == -1) {
            printf("  子进程取消映射失败: %s\n", strerror(errno));
        } else {
            printf("  ✓ 子进程取消映射成功\n");
        }
        
        exit(0);
    } else {
        // 父进程
        printf("父进程 (PID: %d):\n", getpid());
        printf("  创建子进程 PID: %d\n", pid);
        
        // 等待子进程写入数据
        sleep(2);
        
        // 读取子进程写入的数据
        printf("  读取共享数据: %s\n", (char*)shared_map);
        
        // 取消映射
        printf("  父进程取消映射...\n");
        if (munmap(shared_map, map_size) == -1) {
            perror("  父进程取消映射失败");
        } else {
            printf("  ✓ 父进程取消映射成功\n");
        }
        
        // 等待子进程结束
        int status;
        waitpid(pid, &status, 0);
        printf("  子进程已结束\n");
    }
}

int main() {
    printf("=== 匿名和共享内存映射管理 ===\n");
    
    demonstrate_anonymous_mapping();
    demonstrate_shared_memory_unmap();
    
    return 0;
}

示例4:内存映射管理工具 見出しへのリンク

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

typedef struct {
    void *addr;
    size_t size;
    int fd;
    char description[256];
} mapping_info_t;

#define MAX_MAPPINGS 64
static mapping_info_t mappings[MAX_MAPPINGS];
static int mapping_count = 0;

int register_mapping(void *addr, size_t size, int fd, const char *description) {
    if (mapping_count >= MAX_MAPPINGS) {
        return -1;
    }
    
    mappings[mapping_count].addr = addr;
    mappings[mapping_count].size = size;
    mappings[mapping_count].fd = fd;
    strncpy(mappings[mapping_count].description, description, 
            sizeof(mappings[mapping_count].description) - 1);
    mappings[mapping_count].description[sizeof(mappings[mapping_count].description) - 1] = '\0';
    
    return mapping_count++;
}

int unregister_mapping(void *addr) {
    for (int i = 0; i < mapping_count; i++) {
        if (mappings[i].addr == addr) {
            // 将后续映射前移
            for (int j = i; j < mapping_count - 1; j++) {
                mappings[j] = mappings[j + 1];
            }
            mapping_count--;
            return 0;
        }
    }
    return -1;
}

void list_mappings() {
    printf("=== 当前内存映射列表 ===\n");
    
    if (mapping_count == 0) {
        printf("没有活动的内存映射\n");
        return;
    }
    
    printf("%-4s %-14s %-10s %-6s %s\n", "ID", "地址", "大小", "文件描述符", "描述");
    printf("%-4s %-14s %-10s %-6s %s\n", "--", "----", "----", "--------", "----");
    
    for (int i = 0; i < mapping_count; i++) {
        printf("%-4d %p %-10zu %-6d %s\n",
               i, mappings[i].addr, mappings[i].size, 
               mappings[i].fd, mappings[i].description);
    }
    
    printf("总计: %d 个映射\n", mapping_count);
}

void interactive_mapping_manager() {
    int choice;
    char filename[256];
    size_t map_size;
    int fd;
    void *mapped_addr;
    
    while (1) {
        printf("\n=== 内存映射管理工具 ===\n");
        printf("1. 创建文件映射\n");
        printf("2. 创建匿名映射\n");
        printf("3. 取消内存映射\n");
        printf("4. 列出所有映射\n");
        printf("5. 显示系统页面大小\n");
        printf("6. 查看 /proc/self/maps\n");
        printf("0. 退出\n");
        printf("请选择操作: ");
        
        if (scanf("%d", &choice) != 1) {
            printf("输入无效\n");
            while (getchar() != '\n');  // 清空输入缓冲区
            continue;
        }
        
        switch (choice) {
            case 1:
                printf("创建文件映射:\n");
                printf("输入文件名: ");
                scanf("%255s", filename);
                
                fd = open(filename, O_RDWR);
                if (fd == -1) {
                    // 文件不存在,尝试创建
                    fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
                    if (fd == -1) {
                        printf("✗ 无法打开或创建文件: %s\n", strerror(errno));
                        break;
                    }
                    printf("✓ 创建新文件: %s\n", filename);
                } else {
                    printf("✓ 打开现有文件: %s\n", filename);
                }
                
                printf("输入映射大小 (字节): ");
                if (scanf("%zu", &map_size) == 1) {
                    // 设置文件大小
                    if (ftruncate(fd, map_size) == -1) {
                        printf("✗ 设置文件大小失败: %s\n", strerror(errno));
                        close(fd);
                        break;
                    }
                    
                    // 创建映射
                    mapped_addr = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                                      MAP_SHARED, fd, 0);
                    if (mapped_addr == MAP_FAILED) {
                        printf("✗ 创建映射失败: %s\n", strerror(errno));
                        close(fd);
                        break;
                    }
                    
                    char description[256];
                    snprintf(description, sizeof(description), "文件: %s", filename);
                    int id = register_mapping(mapped_addr, map_size, fd, description);
                    if (id != -1) {
                        printf("✓ 成功创建映射 (ID: %d)\n", id);
                        printf("  地址: %p\n", mapped_addr);
                        printf("  大小: %zu 字节\n", map_size);
                    } else {
                        printf("✗ 注册映射失败\n");
                        munmap(mapped_addr, map_size);
                        close(fd);
                    }
                }
                break;
                
            case 2:
                printf("创建匿名映射:\n");
                printf("输入映射大小 (字节): ");
                if (scanf("%zu", &map_size) == 1) {
                    mapped_addr = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
                    if (mapped_addr == MAP_FAILED) {
                        printf("✗ 创建匿名映射失败: %s\n", strerror(errno));
                        break;
                    }
                    
                    int id = register_mapping(mapped_addr, map_size, -1, "匿名映射");
                    if (id != -1) {
                        printf("✓ 成功创建匿名映射 (ID: %d)\n", id);
                        printf("  地址: %p\n", mapped_addr);
                        printf("  大小: %zu 字节\n", map_size);
                    } else {
                        printf("✗ 注册映射失败\n");
                        munmap(mapped_addr, map_size);
                    }
                }
                break;
                
            case 3: {
                if (mapping_count == 0) {
                    printf("没有活动的映射\n");
                    break;
                }
                
                list_mappings();
                printf("输入要取消的映射 ID: ");
                int map_id;
                if (scanf("%d", &map_id) == 1) {
                    if (map_id >= 0 && map_id < mapping_count) {
                        mapping_info_t *map = &mappings[map_id];
                        printf("取消映射: %p (大小: %zu)\n", map->addr, map->size);
                        
                        if (munmap(map->addr, map->size) == 0) {
                            printf("✓ 成功取消映射\n");
                            if (map->fd != -1) {
                                close(map->fd);
                            }
                            unregister_mapping(map->addr);
                        } else {
                            printf("✗ 取消映射失败: %s\n", strerror(errno));
                        }
                    } else {
                        printf("无效的映射 ID\n");
                    }
                }
                break;
            }
            
            case 4:
                list_mappings();
                break;
                
            case 5:
                printf("系统页面大小: %ld 字节\n", (long)getpagesize());
                break;
                
            case 6:
                printf("当前进程内存映射:\n");
                system("cat /proc/self/maps | head -20");
                break;
                
            case 0:
                printf("退出内存映射管理工具\n");
                // 清理所有剩余映射
                for (int i = 0; i < mapping_count; i++) {
                    munmap(mappings[i].addr, mappings[i].size);
                    if (mappings[i].fd != -1) {
                        close(mappings[i].fd);
                    }
                }
                mapping_count = 0;
                return;
                
            default:
                printf("无效选择\n");
                break;
        }
    }
}

void demonstrate_munmap_features() {
    printf("=== munmap 特性演示 ===\n");
    
    printf("munmap 主要特性:\n");
    printf("1. 取消内存映射区域\n");
    printf("2. 释放相关资源\n");
    printf("3. 更新进程地址空间\n");
    printf("4. 支持部分区域取消\n");
    printf("5. 线程安全操作\n");
    
    printf("\n使用注意事项:\n");
    printf("• 地址必须是页面对齐的\n");
    printf("• 长度会被向上舍入到页面边界\n");
    printf("• 取消后原地址不可访问\n");
    printf("• 可能触发页面写回操作\n");
    printf("• 对共享映射影响所有进程\n");
}

int main() {
    printf("=== 内存映射管理工具 ===\n");
    
    // 显示系统信息
    printf("系统信息:\n");
    printf("  页面大小: %ld 字节\n", (long)getpagesize());
    printf("  PID: %d\n", getpid());
    
    // 演示特性
    demonstrate_munmap_features();
    
    // 启动交互式管理器
    char choice;
    printf("\n是否启动交互式内存映射管理器? (y/N): ");
    if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
        interactive_mapping_manager();
    }
    
    return 0;
}

9. munmap 行为说明 見出しへのリンク

// munmap 的重要行为特性:

// 1. 地址对齐
// • addr 参数必须是页面对齐的
// • length 会被向上舍入到页面边界

// 2. 资源释放
// • 释放虚拟地址空间
// • 可能释放物理内存
// • 对于文件映射,可能触发写回

// 3. 共享映射
// • 影响所有映射同一区域的进程
// • 修改对所有进程可见

// 4. 错误处理
// • 失败时不保证部分操作的原子性
// • 需要检查返回值和 errno

// 5. 性能考虑
// • 可能涉及页面写回操作
// • 大量取消操作可能影响性能

10. 实际应用场景 見出しへのリンク

场景1:大文件处理 見出しへのリンク

void process_large_file(const char *filename) {
    int fd = open(filename, O_RDONLY);
    if (fd == -1) return;
    
    struct stat st;
    if (fstat(fd, &st) == -1) {
        close(fd);
        return;
    }
    
    // 分块映射处理大文件
    size_t chunk_size = 1024 * 1024;  // 1MB
    off_t offset = 0;
    
    while (offset < st.st_size) {
        size_t current_size = (st.st_size - offset > chunk_size) ? 
                             chunk_size : (st.st_size - offset);
        
        void *mapped = mmap(NULL, current_size, PROT_READ, MAP_PRIVATE, fd, offset);
        if (mapped != MAP_FAILED) {
            // 处理映射的数据
            process_data(mapped, current_size);
            
            // 立即取消映射释放资源
            munmap(mapped, current_size);
        }
        
        offset += current_size;
    }
    
    close(fd);
}

场景2:动态内存池管理 見出しへのリンク

typedef struct {
    void *addr;
    size_t size;
    int is_mapped;
} memory_pool_t;

memory_pool_t *create_memory_pool(size_t size) {
    memory_pool_t *pool = malloc(sizeof(memory_pool_t));
    if (!pool) return NULL;
    
    pool->addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (pool->addr == MAP_FAILED) {
        free(pool);
        return NULL;
    }
    
    pool->size = size;
    pool->is_mapped = 1;
    return pool;
}

void destroy_memory_pool(memory_pool_t *pool) {
    if (pool && pool->is_mapped) {
        munmap(pool->addr, pool->size);
        pool->is_mapped = 0;
        free(pool);
    }
}

场景3:临时文件映射 見出しへのリンク

void *map_temporary_file(size_t size, int *fd_out) {
    char temp_name[] = "/tmp/mmap_temp_XXXXXX";
    int fd = mkstemp(temp_name);
    if (fd == -1) return MAP_FAILED;
    
    if (ftruncate(fd, size) == -1) {
        close(fd);
        unlink(temp_name);
        return MAP_FAILED;
    }
    
    void *mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        close(fd);
        unlink(temp_name);
        return MAP_FAILED;
    }
    
    // 立即删除文件(仅保留文件描述符)
    unlink(temp_name);
    
    *fd_out = fd;
    return mapped;
}

void unmap_temporary_file(void *addr, size_t size, int fd) {
    munmap(addr, size);
    close(fd);
}

11. 注意事项 見出しへのリンク

使用 munmap 时需要注意:

  1. 地址对齐: 地址必须是页面对齐的
  2. 长度处理: 长度会被向上舍入到页面边界
  3. 资源释放: 确保释放相关的文件描述符
  4. 并发安全: 多线程环境中注意同步
  5. 错误检查: 始终检查返回值和 errno
  6. 性能影响: 大量取消操作可能影响性能
  7. 共享映射: 注意对其他进程的影响

12. 系统配置检查 見出しへのリンク

# 查看系统页面大小
getconf PAGESIZE

# 查看进程内存映射
cat /proc/self/maps

# 查看系统内存信息
cat /proc/meminfo

# 查看虚拟内存统计
vmstat

# 查看内存映射统计
cat /proc/self/smaps | head -20

13. 性能优化建议 見出しへのリンク

// 性能优化建议:

// 1. 批量取消映射
void batch_unmap(void **addrs, size_t *sizes, int count) {
    for (int i = 0; i < count; i++) {
        munmap(addrs[i], sizes[i]);
    }
}

// 2. 及时释放不需要的映射
void *large_data = mmap(NULL, huge_size, PROT_READ, MAP_PRIVATE, fd, 0);
// 使用完后立即释放
process_large_data(large_data);
munmap(large_data, huge_size);

// 3. 合理使用映射粒度
// 避免过于细碎的映射和取消操作

总结 見出しへのリンク

munmap 是 Linux 系统中管理内存映射的重要系统调用:

关键特性:

  1. 资源释放: 释放虚拟地址空间和相关资源
  2. 灵活操作: 支持部分区域取消映射
  3. 共享感知: 正确处理共享内存映射
  4. 系统集成: 与虚拟内存管理系统紧密集成

主要应用:

  1. 大文件处理和数据分析
  2. 动态内存池管理
  3. 共享内存和进程间通信
  4. 高性能数据处理应用

使用要点:

  1. 确保地址页面对齐
  2. 合理管理映射生命周期
  3. 正确处理错误情况
  4. 注意对共享映射的影响
  5. 及时释放不需要的资源

正确使用 munmap 可以帮助应用程序高效管理内存资源,是现代 Linux 系统编程的重要技能。