当前位置: 首页 > news >正文

【进程通信】 Linux下使用共享内存实现跨进程通信:基于C++的完整示例

在 Linux 系统中,不同进程间无法直接共享变量,但我们可以通过 共享内存(Shared Memory) 这种高效的进程间通信方式(IPC)实现数据共享。本文将通过一个基于 C++ 的示例,带你从零理解如何使用共享内存配合信号量实现跨进程通信,并详细解释关键函数如 ftok() 的原理和使用。


一、为什么选择共享内存?

在常见的进程通信方式中,比如管道、消息队列、Socket 等,都存在一定的系统调用开销。而共享内存是最快的IPC方式之一,它允许多个进程将同一块内存段映射到各自的地址空间,从而实现内存级别的数据共享。缺点是它本身不具备同步机制,因此通常需要结合信号量或互斥锁使用。


二、示例代码概览

我们将实现两个进程:

  • 生产者(Producer):将数据写入共享内存。
  • 消费者(Consumer):从共享内存读取数据。

为了避免同时访问造成数据混乱,示例中还使用了 POSIX 信号量(sem_t 来进行互斥访问控制。

代码文件名为:shared_memory_demo.cpp


三、共享内存结构定义

我们定义了一个结构体 SharedData,用于在进程间传递数据:

struct SharedData {
    int counter;
    char message[128];
};

该结构体将在共享内存中作为数据容器,用于写入和读取。


四、共享内存的创建与连接(含 key 生成机制详解)

在 Linux 中使用共享内存前,需通过 shmget() 创建共享内存段。这个函数需要一个 key 来标识这块共享内存,生成这个 key 的常用方法是使用 ftok()

key_t key = ftok(SHM_KEY_PATH, 'R');

🔍 ftok() 原理解析

ftok() 函数用于从一个已有文件路径生成一个唯一的 key。它的原型如下:

key_t ftok(const char* pathname, int proj_id);
  • pathname:指定一个存在的文件路径(必须存在!)。
  • proj_id:一个项目ID,通常为字符(如 'R''X' 等),用于区分不同用途的共享内存。
工作机制:

ftok() 会将指定文件的 inode编号设备号proj_id 组合,生成一个唯一的 key_t 值。例如:

touch /tmp/shm_demo
ls -i /tmp/shm_demo

执行上述命令可以查看该文件的 inode 编号。

使用该 key 创建共享内存:

int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);

成功后即可通过 shmat() 将共享内存映射到当前进程的地址空间。


五、使用信号量进行访问控制

由于共享内存没有内建的同步机制,我们使用 POSIX 信号量 sem_t 来保证互斥访问:

sem_t* sem = sem_open(SEM_NAME, O_CREAT, 0666, 1);
sem_wait(sem);
// 操作共享内存
sem_post(sem);
  • sem_open():打开或创建信号量。
  • sem_wait():进入临界区,信号量减一。
  • sem_post():退出临界区,信号量加一。

六、生产者进程实现

void producer() {
    int shmid = create_shared_memory();
    SharedData* data = (SharedData*)shmat(shmid, NULL, 0);
    sem_t* sem = sem_open(SEM_NAME, O_CREAT, 0666, 1);

    sem_wait(sem);
    data->counter = 0;
    strcpy(data->message, "Hello from Producer, I am T, use the shm of IPC firstly !");
    sem_post(sem);

    std::cout << "Producer wrote: " << data->message << std::endl;

    std::cout << "Press Enter to exit..." << std::endl;
    std::cin.get();

    // 清理资源
    shmdt(data);
    sem_close(sem);
    shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
    sem_unlink(SEM_NAME);          // 删除信号量
}

七、消费者进程实现

void consumer() {
    int shmid = create_shared_memory();
    SharedData* data = (SharedData*)shmat(shmid, NULL, 0);
    sem_t* sem = sem_open(SEM_NAME, 0);

    sem_wait(sem);
    std::cout << "Consumer received: \n"
              << "Counter: " << data->counter << "\n"
              << "Message: " << data->message << std::endl;
    sem_post(sem);

    shmdt(data);
    sem_close(sem);
}

八、程序入口与运行方式

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " [producer|consumer]\n";
        return EXIT_FAILURE;
    }

    if (strcmp(argv[1], "producer") == 0) {
        producer();
    } else if (strcmp(argv[1], "consumer") == 0) {
        consumer();
    } else {
        std::cerr << "Invalid argument\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

✅ 编译和运行:

# 编译
g++ -o shm_demo shared_memory_demo.cpp -pthread

# 创建路径文件供 ftok 使用
touch /tmp/shm_demo

# 终端1:运行生产者
./shm_demo producer

# 终端2:运行消费者
./shm_demo consumer

九、总结

共享内存提供了一种高效的进程间通信机制,但需要开发者自己管理同步问题。本文展示了如何使用 shmgetshmat 等函数创建和使用共享内存,并结合 POSIX 信号量实现互斥访问。同时,通过对 ftok() 机制的讲解,帮助理解共享内存 key 的生成原理和潜在的错误排查方向。

这种方式非常适合用于:

  • 实时通信(如传感器数据共享)
  • 大数据量传输(如图像帧、视频块)
  • 轻量级进程协作场景

相关文章:

  • rancher 采用ingerss ssl 部署nginx+php项目
  • c# 使用NPOI将datatable的数据导出到excel
  • OSPF不规则区域和LSA
  • 【Java学习】AI时代下如何学习Java语言开发
  • 【算法学习计划】回溯 -- 记忆化搜索
  • StringTemplate修仙指南:字符串处理的“言出法随“大法
  • 智能物联网网关策略部署
  • vue3+vite+js项目引入electron构建跨平台桌面应用
  • Excel 自动执行全局宏
  • 项目进度延误的十大原因及应对方案
  • 4-10记录(
  • 聊天室项目Day3之服务器的http的get和post回复实现
  • 软件信息安全性测试如何进行?有哪些注意事项?
  • 神经网络入门—自定义神经网络续集
  • 2. 单词个数统计
  • WPS JS宏编程教程(从基础到进阶)-- 第六部分:JS集合与映射在 WPS 的应用
  • 关于使用@Slf4j后引入log,idea标红解决办法
  • Linux | I.MX6ULL外设功能验证(11)
  • FreeRTOS项目工程完善指南:STM32F103C8T6系列
  • 【结合vue源码,分析vue2及vue3的数据绑定实现原理】
  • 徐州企业网站建设公司/关系网站优化公司
  • 如何制作自己网站/cms网站
  • 学习css网站开发/常州百度seo排名
  • 做游戏类型的网站的好处/保定seo网站推广
  • 网页无法访问 wordpress/湖南正规关键词优化首选
  • 济南开发网站/今天发生的新闻