【进程间通信原理2】SystemV 共享内存、消息队列、信号量详解与代码实例

前言 - 进程间通信与管道的理解

关于进程间通信原理中【进程间通信】与【管道】的理解,看之前的文章:

进程间通信 与 管道


2. system V共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

2.1 共享内存 示意图

在这里插入图片描述

2.2 共享内存 数据结构

struct shmid_ds 用于管理在类Unix操作系统中的共享内存段。这个结构通常包含关于共享内存段的信息,包括其键值、大小和各种状态标志。下面是一个典型的 struct shmid_ds 结构定义:

struct shmid_ds {
    struct ipc_perm shm_perm; /* 拥有者和权限结构 */
    size_t shm_segsz;         /* 段的大小(以字节为单位) */
    time_t shm_atime;         /* 最后一次挂载时间 */
    time_t shm_dtime;         /* 最后一次卸载时间 */
    time_t shm_ctime;         /* 最后一次更改时间 */
    pid_t shm_cpid;           /* 创建者的进程ID */
    pid_t shm_lpid;           /* 最后一个操作者的进程ID */
    unsigned short shm_nattch;/* 当前挂载的进程数 */
};

让我们逐个解释每个字段:

  • shm_perm:这是一个类型为 struct ipc_perm 的结构,用于保存共享内存段的拥有权和权限信息。

  • shm_segsz:表示共享内存段的大小,以字节为单位。

  • shm_atime:记录了最后一次段被挂载(连接到进程)的时间。

  • shm_dtime:记录了最后一次段被卸载(从进程断开)的时间。

  • shm_ctime:记录了最后一次段被修改的时间。

  • shm_cpid:存储了最初创建该段的进程ID。

  • shm_lpid:记录了最后一个操作该段的进程的进程ID(例如,挂载或卸载的进程)。

  • shm_nattch:表示当前附加到该段的进程数(shm_nattch 的类型通常是 unsigned short)。


2.3 共享内存函数

当然可以,下面是一个表格形式的表示方式,涵盖了共享内存的主要函数及其说明:

函数名描述参数返回值
shmget创建或获取一个共享内存段key_t key: 键值
size_t size: 大小
int shmflg: 标志
成功时返回共享内存标识符,失败时返回 -1
shmat将共享内存段连接到当前进程的地址空间int shmid: 共享内存标识符
const void *shmaddr: 建议地址
int shmflg: 标志
成功时返回连接后的共享内存地址,失败时返回 (void*)-1
shmdt从当前进程的地址空间中分离已连接的共享内存段const void *shmaddr: 要分离的共享内存地址成功时返回0,失败时返回 -1
shmctl对共享内存段进行控制操作int shmid: 共享内存标识符
int cmd: 操作类型
struct shmid_ds *buf: 信息缓冲区
成功时返回操作相关的值,失败时返回 -1

2.4 示例代码

下面写一个简单的实例代码,演示这些函数的使用(代码创建一个共享内存, 支持两个进程进行通信):

  1. 进程A:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>

// processA.cpp
int main() {
    // 定义键值和共享内存大小
    key_t key = ftok("shmfile", 65);
    int shm_id = shmget(key, 1024, 0666|IPC_CREAT);
    if (shm_id == -1) {
        perror("shmget failed");
        return 1;
    }

    // 将共享内存段附加到当前进程的地址空间
    char *str = (char*) shmat(shm_id, nullptr, 0);
    if (str == (char*)-1) {
        perror("shmat failed");
        return 1;
    }


    // 向共享内存写入数据
    const char* message = "I am process A";
    strncpy(str, message, 1024);


    std::cout << "Process A wrote: " << message << std::endl;


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

    return 0;
}
  1. 进程B:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>



// processB.cpp
int main() {
    // 定义键值和共享内存大小
    key_t key = ftok("shmfile", 65);
    int shm_id = shmget(key, 1024, 0666|IPC_CREAT);
    if (shm_id == -1) {
        perror("shmget failed");
        return 1;
    }

    // 将共享内存段附加到当前进程的地址空间
    char *str = (char*) shmat(shm_id, nullptr, 0);
    if (str == (char*)-1) {
        perror("shmat failed");
        return 1;
    }

    // 从共享内存读取数据并打印
    std::cout << "Process B read: " << str << std::endl;

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

    // 删除共享内存段
    if (shmctl(shm_id, IPC_RMID, nullptr) == -1) {
        perror("shmctl failed");
        return 1;
    }

    return 0;
}
  • 结果演示:

在这里插入图片描述


3. system V 消息队列

System V 消息队列(Message Queue)是一种进程间通信(IPC)机制,允许一个或多个进程以消息的形式进行数据交换。消息队列提供了一种将消息插入到队列中的方法,并能按照消息类型从队列中读取消息。

3.1 主要API函数

  1. msgget: 创建一个新的消息队列或获取一个现有的消息队列。
  2. msgsnd: 向消息队列发送消息。
  3. msgrcv: 从消息队列接收消息。
  4. msgctl: 控制消息队列,如删除消息队列、获取消息队列的状态等。

3.2 使用示例

下面用实例,展示如何在两个进程之间使用 System V 消息队列进行通信。第一个进程发送消息,第二个进程接收消息。

  1. 发送消息的进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_KEY 1234

/*
 struct msgbuf {
    long mtype;      /* 消息类型,必须为正整数 */
    char mtext[100]; /* 消息正文 */
};
*/

int main() {
    int msgid;
    struct msgbuf msg;

    /* 创建消息队列 */
    if ((msgid = msgget(MSG_KEY, IPC_CREAT | 0666)) == -1) {
        perror("msgget");
        exit(1);
    }

    /* 准备消息 */
    msg.mtype = 1;  /* 消息类型 */
    strcpy(msg.mtext, "Hello, this is a test message!");

    /* 发送消息 */
    if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
        perror("msgsnd");
        exit(1);
    }

    printf("Message sent: %s\n", msg.mtext);

    return 0;
}
  1. 接收消息的进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

#define MSG_KEY 1234

int main() {
    int msgid;
    struct msgbuf msg;

    /* 获取消息队列 */
    if ((msgid = msgget(MSG_KEY, 0666)) == -1) {
        perror("msgget");
        exit(1);
    }

    /* 接收消息 */
    if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
        perror("msgrcv");
        exit(1);
    }

    printf("Message received: %s\n", msg.mtext);

    /* 删除消息队列 */
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("msgctl");
        exit(1);
    }

    return 0;
}

4. system V 信号量

System V 信号量是 UNIX 系统中一种用于进程间同步和互斥的机制它们是 System V IPC(进程间通信)的一部分,主要用于管理共享资源的访问,确保多个进程能够协调工作而不会发生冲突。

4.2 基本概念 && 操作

  1. 基本概念

    • 信号量(Semaphore):一个信号量是一个计数器,用于控制对共享资源的访问。它可以表示资源的可用数量或保证某个操作的有序执行。
    • 信号量集(Semaphore Set):System V 信号量通常以集的形式出现,一个信号量集可以包含一个或多个信号量。
  2. 操作

    • P 操作(wait/decrement):即减操作,试图将信号量的值减 1。如果信号量的值已经是 0,则调用进程会被阻塞,直到信号量的值大于 0 为止。
    • V 操作(signal/increment):即加操作,将信号量的值加 1。如果有其他进程在等待该信号量(因为执行 P 操作被阻塞),则其中一个进程会被唤醒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值