Linux使用mmap实现父子进程间的通信

 更新时间:2025年11月27日 09:23:40   作者:郝学胜-神的一滴  
在Linux系统中,进程间通信(IPC)是编程中常见的需求,传统的IPC方式包括管道、消息队列、共享内存、信号量等,mmap函数可以将文件映射到进程的地址空间,也可以用于创建匿名映射,从而实现进程间的内存共享,本文将详细介绍如何在Linux中使用mmap实现父子进程间的通信

引言

在Linux系统中,进程间通信(IPC)是编程中常见的需求。传统的IPC方式包括管道、消息队列、共享内存、信号量等。其中,共享内存是一种高效的IPC方式,因为它允许不同进程直接读写同一块内存区域,而不需要内核的介入。mmap(memory map)函数可以将文件映射到进程的地址空间,也可以用于创建匿名映射,从而实现进程间的内存共享。本文将详细介绍如何在Linux中使用mmap实现父子进程间的通信。

一、mmap基本概念

mmap是Linux系统提供的一个系统调用,用于将文件或设备映射到进程的地址空间。通过mmap,进程可以像访问内存一样访问文件或设备,而无需使用传统的read/write系统调用。

mmap函数原型如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数说明:

  • addr:指定映射的起始地址,通常设为NULL让系统自动选择
  • length:映射区域的长度
  • prot:保护标志,如PROT_READ(可读)、PROT_WRITE(可写)等
  • flags:映射类型标志,如MAP_SHARED(共享)、MAP_PRIVATE(私有)等
  • fd:文件描述符,��于匿名映射设为-1
  • offset:映射的文件偏移量,通常为0

mmap成功时返回映射区域的起始地址,失败时返回MAP_FAILED((void*)-1)。

二、父子进程创建方式

在Linux中,可以通过多种方式创建父子进程:

  1. fork():创建一个与父进程几乎完全相同的子进程
  2. vfork():创建一个与父进程共享地址空间的子进程
  3. clone():更灵活地创建进程,可以指定共享的资源

最常用的是fork()函数,它创建的子进程是父进程的一个副本,拥有独立的地址空间。但是,通过mmap创建的匿名映射区域可以在父子进程间共享。

三、使用mmap实现父子进程通信

下面我们通过一个示例来展示如何使用mmap实现父子进程间的通信:

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

#define SHM_SIZE 1024

int main() {
    // 使用mmap创建匿名映射
    void *shared_mem = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared_mem == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程代码
        printf("Child process writing to shared memory\n");
        strcpy((char *)shared_mem, "Hello from child process!");
        
        // 子进程结束
        exit(EXIT_SUCCESS);
    } else {
        // 父进程代码
        // 等待子进程结束
        wait(NULL);
        
        printf("Parent process reading from shared memory: %s\n", (char *)shared_mem);
        
        // 解除映射
        if (munmap(shared_mem, SHM_SIZE) == -1) {
            perror("munmap failed");
            exit(EXIT_FAILURE);
        }
    }
    
    return 0;
}

代码解析:

  1. 首先使用mmap创建一个大小为1024字节的匿名映射区域,设置保护标志为PROT_READ | PROT_WRITE,表示可读可写;设置映射类型为MAP_SHARED | MAP_ANONYMOUS,表示这是一个共享的匿名映射。
  2. 使用fork()创建子进程。子进程会继承父进程的地址空间,包括mmap创建的共享内存区域。
  3. 在子进程中,向共享内存写入一条消息:“Hello from child process!”。
  4. 父进程通过wait()等待子进程结束,然后从共享内存中读取子进程写入的消息并打印。
  5. 最后,使用munmap()解除映射,释放共享内存资源。

四、注意事项和最佳实践

  1. 同步问题:由于多个进程可以同时访问共享内存,可能会出现数据竞争问题。可以使用信号量、互斥锁等同步机制来保证数据的一致性。
  2. 内存保护:合理设置mmap的保护标志,避免不必要的读写权限,提高安全性。
  3. 错误处理:对mmap、fork等系统调用进行充分的错误检查,确保程序的健壮性。
  4. 资源释放:确保在程序结束前调用munmap释放映射的内存区域,避免内存泄漏。
  5. 大小选择:根据实际需求选择合适的共享内存大小,避免浪费或不足。

五、扩展应用

mmap不仅可以用于父子进程间通信,还可以用于:

  1. 无亲缘关系的进程间通信:通过将文件映射到内存,不同进程可以访问同一个文件实现通信。
  2. 内存映射文件:将大文件映射到内存,可以高效地访问文件内容,而不需要将整个文件读入内存。
  3. 虚拟内存管理:mmap是Linux虚拟内存管理系统的重要组成部分,用于管理进程的地址空间。

六、进阶示例:带同步的父子进程通信

下面是一个更复杂的示例,展示了如何在父子进程间使用mmap和信号量进行同步通信:

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

#define SHM_SIZE 1024
#define SEM_NAME "/my_semaphore"

typedef struct {
    char message[SHM_SIZE];
    int ready;
} SharedData;

int main() {
    // 创建或打开信号量
    sem_t *sem = sem_open(SEM_NAME, O_CREAT, 0644, 0);
    if (sem == SEM_FAILED) {
        perror("sem_open failed");
        exit(EXIT_FAILURE);
    }
    
    // 使用mmap创建匿名映射
    SharedData *shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared_data == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }
    
    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程代码
        printf("Child process writing to shared memory\n");
        strcpy(shared_data->message, "Hello from child process with synchronization!");
        shared_data->ready = 1;
        
        // 通知父数据已准备好
        if (sem_post(sem) == -1) {
            perror("sem_post failed");
            exit(EXIT_FAILURE);
        }
        
        // 子进程结束
        exit(EXIT_SUCCESS);
    } else {
        // 父进程代码
        // 等待子进程通知
        if (sem_wait(sem) == -1) {
            perror("sem_wait failed");
            exit(EXIT_FAILURE);
        }
        
        printf("Parent process reading from shared memory: %s\n", shared_data->message);
        
        // 解除映射
        if (munmap(shared_data, sizeof(SharedData)) == -1) {
            perror("munmap failed");
            exit(EXIT_FAILURE);
        }
        
        // 关闭并删除信号量
        if (sem_close(sem) == -1) {
            perror("sem_close failed");
            exit(EXIT_FAILURE);
        }
        
        if (sem_unlink(SEM_NAME) == -1) {
            perror("sem_unlink failed");
            exit(EXIT_FAILURE);
        }
    }
    
    return 0;
}

这个示例展示了如何使用信号量来同步父子进程对共享内存的访问,确保父进程在子进程写入数据后才能读取数据。

七、总结

mmap是一种高效的进程间通信方式,特别适合需要大量数据交换的场景。通过合理使用mmap,可以实现父子进程间的高效通信。本文介绍了mmap的基本概念、父子进程创建方式,以及使用mmap实现父子进程通信的具体方法和注意事项。希望读者能够通过本文掌握mmap的使用,并在实际编程中灵活应用。

以上就是关于Linux父子进程使用mmap通信的详细介绍。mmap作为一种高效的IPC机制,在系统编程中有着广泛的应用。通过合理使用mmap,可以大大提高进程间通信的效率,减少数据拷贝的开销。

以上就是Linux使用mmap实现父子进程间的通信的详细内容,更多关于Linux mmap父子进程通信的资料请关注脚本之家其它相关文章!

相关文章

  • Linux上进行常用软件的配置方法

    Linux上进行常用软件的配置方法

    这篇文章主要介绍了Linux上进行常用软件的配置方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-08-08
  • 详解linux系统下pid的取值范围

    详解linux系统下pid的取值范围

    这篇文章主要介绍了详解linux系统下pid的取值范围的相关资料,需要的朋友可以参考下
    2018-07-07
  • Ubuntu 20.04 开启隐藏录音降噪功能(推荐)

    Ubuntu 20.04 开启隐藏录音降噪功能(推荐)

    这篇文章主要介绍了Ubuntu 20.04 开启隐藏录音降噪功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Linux免密码远程登录的实例

    Linux免密码远程登录的实例

    这篇文章主要介绍了Linux免密码远程登录的实例的相关资料,需要的朋友可以参考下
    2017-06-06
  • 解读crontab脚本错误日志和正确的输出写入到文件

    解读crontab脚本错误日志和正确的输出写入到文件

    这篇文章主要介绍了解读crontab脚本错误日志和正确的输出写入到文件问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • centos更改时区的方法

    centos更改时区的方法

    centos默认使用UTC时区,中国用户需要修改成自己的时区,这篇文章主要介绍了centos更改时区的方法,需要的朋友可以参考下
    2014-03-03
  • Ubuntu 16.04备份和恢复小结

    Ubuntu 16.04备份和恢复小结

    一开始想借助于第三方的备份恢复工具来完成当前的Ubuntu 16.04系统的备份以及后面的还原工作,不过考虑到那样做比较麻烦,索性就使用tar工具直接备份得了。这篇文章给大家详细介绍了Ubuntu 16.04备份和恢复的方法,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-11-11
  • linux应用软件编程之多任务(进程)详解

    linux应用软件编程之多任务(进程)详解

    文章介绍了进程与多任务的概念,涵盖进程定义、状态(运行、就绪、等待、僵尸等)、调度算法及资源回收机制,并列举了ps、top、kill等系统命令和fork、exit等编程方法,说明如何创建、管理及终止进程
    2025-08-08
  • Centos下配置Redis开机启动脚本

    Centos下配置Redis开机启动脚本

    本篇文章主要介绍了redis之Centos下配置Redis开机启动脚本,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 使用 Linux seq 命令生成数字序列(推荐)

    使用 Linux seq 命令生成数字序列(推荐)

    seq命令用于以指定增量从首数开始打印数字到尾数,即产生从某个数到另外一个数之间的所有整数,并且可以对整数的格式、宽度、分割符号进行控制。这篇文章主要介绍了使用 Linux seq 命令生成数字序列,需要的朋友可以参考下
    2020-01-01

最新评论