进程间通信——共享内存

一、共享内存是什么?

想象两个程序(进程)需要交换数据,共享内存就像在白板上写字:

  • 创建一块公共内存区域(白板)

  • 两个进程都能直接读写这块内存

  • 最快的进程通信方式(省去数据复制步骤)

二、为什么用共享内存?
通信方式速度复杂度数据拷贝次数
管道简单2次
消息队列中等中等2次
共享内存较高0次
三、核心API详解(附代码)
1. shmget - 创建/获取共享内存
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
  • 参数

    • key:唯一标识符,常用ftok("文件路径", 字符)生成

    • size:共享内存大小(字节)

    • shmflg:权限标志,如 IPC_CREAT | 0666

  • 返回值:共享内存ID(成功时),-1(失败)

// 示例:创建1KB共享内存
key_t key = ftok("/tmp", 'A');
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
if(shmid == -1) {
    perror("shmget失败");
    exit(1);
}
2. shmat - 连接共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数

    • shmidshmget返回的ID

    • shmaddr:连接地址(通常填NULL让系统自动分配)

    • shmflg0(读写)或SHM_RDONLY(只读)

  • 返回值:共享内存首地址(成功时),(void*)-1(失败)

char *shm_ptr = (char*)shmat(shmid, NULL, 0);
if(shm_ptr == (char*)-1) {
    perror("连接失败");
    exit(1);
}
3. shmdt - 断开连接
int shmdt(const void *shmaddr);
// 示例:断开连接
if(shmdt(shm_ptr) == -1) {
    perror("断开失败");
}
4. shmctl - 控制操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 常用操作

    • 删除:shmctl(shmid, IPC_RMID, NULL)

    • 查看状态:shmctl(shmid, IPC_STAT, &buf)

// 示例:删除共享内存
if(shmctl(shmid, IPC_RMID, NULL) == -1) {
    perror("删除失败");
}
四、完整通信流程

五、实战代码示例

writer.c(写入方)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main() {
    // 生成唯一key
    key_t key = ftok("/tmp", 'x');
    
    // 创建共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if(shmid == -1) {
        perror("shmget失败");
        exit(1);
    }
    
    // 连接共享内存
    char *data = shmat(shmid, NULL, 0);
    if(data == (char*)-1) {
        perror("连接失败");
        exit(1);
    }
    
    // 写入数据
    printf("写入消息: ");
    fgets(data, SHM_SIZE, stdin);
    printf("已写入: %s\n", data);
    
    // 等待读取方完成
    printf("按回车删除共享内存...\n");
    getchar();
    
    // 清理
    shmdt(data);
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

reader.c(读取方)

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main() {
    // 生成相同key
    key_t key = ftok("/tmp", 'x');
    
    // 获取共享内存
    int shmid = shmget(key, SHM_SIZE, 0666);
    if(shmid == -1) {
        perror("获取失败");
        exit(1);
    }
    
    // 连接共享内存
    char *data = shmat(shmid, NULL, SHM_RDONLY);
    if(data == (char*)-1) {
        perror("连接失败");
        exit(1);
    }
    
    // 读取数据
    printf("读取到消息: %s\n", data);
    
    // 断开连接
    shmdt(data);
    return 0;
}
六、必须知道的注意事项
  1. 同步问题(最重要!)

    • 共享内存没有内置锁机制

    • 必须用额外同步工具:

      // 推荐方案:信号量
      sem_t *sem = sem_open("/mysem", O_CREAT, 0666, 1);
      sem_wait(sem); // 进入临界区
      /* 操作共享内存 */
      sem_post(sem); // 离开临界区
  2. 内存生命周期

    • 进程退出后共享内存仍然存在

    • 必须手动删除(shmctl + IPC_RMID

    • 查看系统共享内存:ipcs -m

    • 手动删除:ipcrm -m [shmid]

  3. 键值冲突预防

    • 使用ftok生成key的要点:

    key_t ftok(const char *path, int proj_id);
    • path必须存在且可访问

    • proj_id用非0字符(如'A')

    内存对齐问题

    • 不同进程访问结构体时需对齐:

    struct SharedData {
        int id;
        char name[20];
    } __attribute__((aligned(8))); // 8字节对齐

    安全建议

    • 设置最小必要权限(如0600)

    • 避免存储敏感数据

    • 使用shmctl(SHM_LOCK)锁定物理内存

七、常见错误排查
错误现象可能原因解决方案
Permission denied权限不足检查shmflg(如0666)
Invalid argument大小不合法确保size>0
File exists已存在相同key的内存使用IPC_EXCL检测
读取到乱码未同步写入完成添加同步机制
No space left on device系统共享内存上限调整/proc/sys/kernel/shmmax
八、最佳实践总结
  1. 创建流程
    ftok → shmget → shmat → 使用 → shmdt → shmctl(删除)

  2. 连接技巧

    // 只读模式连接(保护数据)
    void *ptr = shmat(shmid, NULL, SHM_RDONLY);
    
    // 指定地址连接(特殊需求)
    void *ptr = shmat(shmid, (void*)0x500000, 0);
  3. 实时监控命令

    # 查看共享内存
    ipcs -m
    
    # 删除所有共享内存(危险!)
    ipcrm -a
  4. 大小限制检查

    # 查看系统限制
    cat /proc/sys/kernel/shmmax  # 最大单块内存
    cat /proc/sys/kernel/shmall  # 总内存限制

📌 终极提示:共享内存像一把双刃剑 - 用好了速度飞起,用错了灾难重重。务必配合同步机制使用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hardStudy_h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值