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;
}
相关推荐
小卒过河010419 分钟前
使用apache nifi 从数据库文件表路径拉取远程文件至远程服务器目的地址
运维·服务器·数据库
橘颂TA22 分钟前
【剑斩OFFER】算法的暴力美学——翻转对
算法·排序算法·结构与算法
叠叠乐24 分钟前
robot_state_publisher 参数
java·前端·算法
土星云SaturnCloud32 分钟前
液冷“内卷”:在局部优化与系统重构之间,寻找第三条路
服务器·人工智能·ai·计算机外设
hweiyu0034 分钟前
排序算法:冒泡排序
算法·排序算法
叶之香1 小时前
CentOS/RHEL 7、8安装exfat和ntfs文件系统
linux·运维·centos
brave and determined1 小时前
CANN训练营 学习(day9)昇腾AscendC算子开发实战:从零到性能冠军
人工智能·算法·机器学习·ai·开发环境·算子开发·昇腾ai
一世琉璃白_Y1 小时前
pg配置国内数据源安装
linux·python·postgresql·centos
不过普通话一乙不改名1 小时前
Linux 内核开发入门:从环境配置到 Hello World 实战
linux·运维