Linux——进程间通信(system V共享内存)

system V共享内存

  • [system V共享内存](#system V共享内存)
    • 概念
    • [System V 共享内存的定义方式](#System V 共享内存的定义方式)
    • 函数详解
    • [System V 共享内存的本质](#System V 共享内存的本质)
    • [System V 共享内存与匿名管道的不同](#System V 共享内存与匿名管道的不同)
    • [System V 共享内存的原理与底层解读](#System V 共享内存的原理与底层解读)
    • 分步操作案例
    • 通信案例

system V共享内存

概念

System V 共享内存是一种进程间通信(IPC)机制,允许多个进程共享同一块内存区域。与管道不同,共享内存不涉及内核缓冲区,而是直接映射到进程的地址空间,因此速度非常快。

System V 共享内存的定义方式

(1)在代码中创建共享内存

在 C 代码中,可以使用以下系统调用来操作共享内存:

  • shmget():创建或获取共享内存段。

  • shmat():将共享内存段附加到进程的地址空间。

  • shmdt():将共享内存段从进程的地址空间分离。

  • shmctl():控制共享内存段(如删除)。

函数详解

(1)ftok()

生成一个唯一的键值,用于标识共享内存段。

函数模型:

c 复制代码
key_t ftok(const char *pathname, int proj_id);

参数:

  • pathname:文件路径名(必须存在且可访问)。

  • proj_id:项目 ID(通常为一个字符)。

返回值:

  • 成功:返回生成的键值。

  • 失败:返回 -1。

 (2) shmget() 

创建或获取共享内存段。

函数模型:

c 复制代码
int shmget(key_t key, size_t size, int shmflg);

参数:

  • key:共享内存段的键值(通常由 ftok() 生成)。

  • size:共享内存段的大小(以字节为单位)。

  • shmflg:标志位,通常为权限模式(如 0666)与 IPC_CREAT 的组合。

返回值:

  • 成功:返回共享内存段的标识符(shmid)。

  • 失败:返回 -1。

(3)shmat()

将共享内存段附加到进程的地址空间。

函数模型:

c 复制代码
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

  • shmid:共享内存段的标识符。

  • shmaddr:指定附加地址(通常为 NULL,由系统自动选择)。

  • shmflg:标志位(通常为 0)。

返回值:

  • 成功:返回共享内存段的起始地址。

  • 失败:返回 (void*) -1。

(4)shmdt()

将共享内存段从进程的地址空间分离。

函数模型:

c 复制代码
int shmdt(const void *shmaddr);

参数:

  • shmaddr:共享内存段的起始地址(由 shmat() 返回)。

返回值:

  • 成功:返回 0。

  • 失败:返回 -1。

(5)shmctl()

控制共享内存段(如删除)。

函数模型:

c 复制代码
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

  • shmid:共享内存段的标识符。

  • cmd:控制命令(如 IPC_RMID 用于删除)。

  • buf:指向 struct shmid_ds 的指针(通常为 NULL)。

返回值:

  • 成功:返回 0。

  • 失败:返回 -1。

System V 共享内存的本质

(1)内核中的共享内存段

共享内存段是内核中的一块内存区域,由内核分配和管理。

每个共享内存段有一个唯一的标识符(shmid)。

(2)进程地址空间映射

进程通过 shmat() 将共享内存段映射到自己的地址空间。

映射后,进程可以直接访问共享内存中的数据。

(3)同步机制

共享内存本身不提供同步机制,需要结合信号量或互斥锁来避免竞争条件。

System V 共享内存与匿名管道的不同

特性 System V 共享内存 管道(Pipe)
通信方式 直接共享内存区域 通过内核缓冲区传递数据
速度 非常快,无需内核介入 较慢,涉及内核缓冲区拷贝
同步机制 需要额外同步机制(如信号量) 无需额外同步机制
进程关系 可用于无亲缘关系的进程 仅用于具有亲缘关系的进程
生命周期 持久存在,直到显式删除 随进程结束而销毁
使用场景 大数据量、高性能要求的通信 小数据量、简单通信
通信方向 双向 单向
缓冲区大小 由用户指定 通常为 64KB
内核实现 通过 struct shmid_kernel 管理 通过 pipe_inode_info 管理
适用性 适用于无亲缘关系的进程 适用于有亲缘关系的进程
删除方式 使用 shmctl() 显式删除 随进程结束自动销毁

System V 共享内存的原理与底层解读

(1)内核数据结构

共享内存段在内核中通过 struct shmid_kernel 管理。

每个共享内存段包含以下信息:

  • 唯一标识符(shmid)。

  • 内存大小。

  • 附加的进程数。

  • 权限信息。

(2)内存映射

进程通过 shmat() 将共享内存段映射到自己的地址空间。

映射后,进程可以直接读写共享内存。

(3)同步机制

共享内存本身不提供同步机制,需要结合信号量或互斥锁来避免竞争条件。

分步操作案例

以下是一个完整的案例,分为 创建、挂载 和 销毁 三个部分。

(1)创建共享内存段

c 复制代码
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
    // 生成唯一键值
    key_t key = ftok("shmfile", 65);
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 创建共享内存段
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    printf("Shared memory created with ID: %d\n", shmid);
    return 0;
}

(2)挂载共享内存段并写入数据

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main() {
    // 生成唯一键值
    key_t key = ftok("shmfile", 65);
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 获取共享内存段
    int shmid = shmget(key, 1024, 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    // 挂载共享内存段
    char *str = (char*) shmat(shmid, (void*)0, 0);
    if (str == (char*) -1) {
        perror("shmat");
        return 1;
    }

    // 写入数据到共享内存
    printf("Writer: Enter data to write: ");
    fgets(str, 1024, stdin);
    printf("Writer: Data written to shared memory: %s\n", str);

    // 分离共享内存段
    if (shmdt(str) == -1) {
        perror("shmdt");
        return 1;
    }

    return 0;
}

(3)挂载共享内存段并读取数据,最后销毁

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
    // 生成唯一键值
    key_t key = ftok("shmfile", 65);
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 获取共享内存段
    int shmid = shmget(key, 1024, 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    // 挂载共享内存段
    char *str = (char*) shmat(shmid, (void*)0, 0);
    if (str == (char*) -1) {
        perror("shmat");
        return 1;
    }

    // 读取数据
    printf("Reader: Data read from shared memory: %s\n", str);

    // 分离共享内存段
    if (shmdt(str) == -1) {
        perror("shmdt");
        return 1;
    }

    // 销毁共享内存段
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        return 1;
    }

    printf("Shared memory destroyed.\n");
    return 0;
    }

通信案例

(1)写入进程(writer.c)

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main() {
    key_t key = ftok("shmfile", 65); // 生成唯一键值
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // 创建共享内存段
    char *str = (char*) shmat(shmid, (void*)0, 0); // 附加共享内存段

    printf("Writer: Enter data to write: ");
    fgets(str, 1024, stdin); // 写入数据到共享内存

    printf("Writer: Data written to shared memory: %s\n", str);
    shmdt(str); // 分离共享内存段
    return 0;
}

(2)读取进程(reader.c)

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
    key_t key = ftok("shmfile", 65); // 生成唯一键值
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // 获取共享内存段
    char *str = (char*) shmat(shmid, (void*)0, 0); // 附加共享内存段

    printf("Reader: Data read from shared memory: %s\n", str);
    shmdt(str); // 分离共享内存段
    shmctl(shmid, IPC_RMID, NULL); // 删除共享内存段
    return 0;
}
相关推荐
Bruce Jue22 分钟前
算法刷题--贪心算法
算法·贪心算法
路由侠内网穿透1 小时前
本地部署资源聚合搜索神器 Jackett 并实现外部访问
linux·运维·服务器·网络协议·tcp/ip
慕容魏2 小时前
入门到入土,Java学习 day16(算法1)
java·学习·算法
认真的小羽❅2 小时前
动态规划详解(二):从暴力递归到动态规划的完整优化之路
java·算法·动态规划
啥都想学的又啥都不会的研究生2 小时前
Redis设计与实现-服务器中的数据库
运维·服务器·数据库·redis·笔记·缓存·性能优化
djykkkkkk2 小时前
ubuntu 和 RV1126 交叉编译Mosqutiio-1.6.9
linux·运维·ubuntu
大小科圣3 小时前
Tomcat介绍及部署
运维·服务器
LiDAR点云3 小时前
Matlab中快速查找元素索引号
数据结构·算法·matlab
CYRUS_STUDIO3 小时前
安卓逆向魔改版 Base64 算法还原
android·算法·逆向
好多知识都想学3 小时前
第二章Linux 命令概述
linux·运维·服务器