Linux进程间通信---使用【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信

IPC结合实现进程间通信实例

下面将使用【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信。

  • 共享内存用来传递数据;
  • 信号量用来同步;
  • 消息队列用来 在客户端修改了共享内存后通知服务器读取。

server.c:服务端接收信息

#include <sys/ipc.h>    // 包含进程间通信相关的头文件
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义消息队列中的消息结构
struct msgbuf {
    long mtype; // 消息类型
    char mtext;  // 消息文本
};

// 定义信号量联合体,用于semctl函数
union semun {
    int val;            // 用于SETVAL命令
    struct semid_ds *buf; // 用于IPC_STAT和IPC_SET命令
};

// 删除IPC资源的函数
void delet_IPC(int msgid, char *shm, int shmid, int semid) {
    shmdt(shm);// 断开共享内存 
    // 删除共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1){
        perror("Failed to delete shared memory");
    }
        // 删除消息队列
	if (msgctl(msgid, IPC_RMID, NULL) == -1){
        perror("Failed to delete message queue");
    }
    // 删除信号量集
    if (semctl(semid, 0, IPC_RMID) == -1){
        perror("Failed to delete semaphore");
    }
}

// P操作:等待信号量
void p_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = -1; // 执行P操作
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

// V操作:释放信号量
void v_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = 1; // 执行V操作
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

int main() {
    key_t key;              // 用于ftok的键
    char *shm;               // 共享内存的指针
    int flag = 1;           // 控制循环的标记
    int msgid, shmid, semid; // 消息队列、共享内存和信号量的ID
    struct msgbuf rcvmsg;   // 定义接收消息的缓冲区
    union semun initsem;	// 定义信号量的初始化联合体

    // 获取或创建key    
    if ((key = ftok(".", 'a')) == -1){
        perror("ftok failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取消息队列
    if ((msgid = msgget(key, IPC_CREAT | 0666)) == -1) {
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取共享内存
   
    if ((shmid = shmget(key, 1024, IPC_CREAT | 0666)) == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }
    // 挂载共享内存 
    if ((shm = (char *)shmat(shmid, 0, 0)) == (void *)(-1)) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }
    // 创建或获取信号量集
    if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) {
        perror("semget failed");
        exit(EXIT_FAILURE);
    }
    // 初始化信号量的值为1
    initsem.val = 1;
    if (semctl(semid, 0, SETVAL, initsem) == -1) {
        perror("semctl SETVAL failed");
    }

    while (flag) {
        // 接收消息
        msgrcv(msgid, &rcvmsg, sizeof(struct msgbuf), 888, 0);
        // 根据消息内容执行不同的操作
        switch (rcvmsg.mtext) {
            case 'r': // 读操作
                printf("read: ");
                p_handler(semid); // 执行P操作
                printf("%s\n", shm); // 打印共享内存中的内容
                v_handler(semid); // 执行V操作
                break;

            case 'q': // 退出操作
                printf("quit\n");
                // 删除所有IPC资源
                delet_IPC(msgid, shm, shmid, semid);
                flag = 0; // 设置退出标志
                break;
        }
    }
    return 0;
}

client.c客户端发送消息

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

struct msgbuf {
    long mtype; // 消息类型
    char mtext; // 消息文本
};

// P操作:等待信号量,直到信号量的值大于0,然后将其减1
void p_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = -1;
    set.sem_flg = SEM_UNDO;
    semop(semid, &set, 1);
}

// V操作:释放信号量,将其值加1,并可能唤醒等待该信号量的其他进程
void v_handler(int semid) {
    struct sembuf set;
    set.sem_num = 0;
    set.sem_op = 1;
    set.sem_flg = SEM_UNDO;
    if (semop(semid, &set, 1) == -1){
        perror("V operation failed");
    }
}

int main() {
    key_t key;
    char *shm;
    char str[128];
    int flag = 1;
    int msgid, shmid, semid;
    struct msgbuf readmsg;

    // 创建或获取IPC的键
  	if ((key = ftok(".", 'a')) < 0){
        perror("ftok failed");
        exit(EXIT_FAILURE);
    }
    // 获取消息队列ID
    if ((msgid = msgget(key, 0666 | IPC_CREAT)) == -1){
        perror("msgget failed");
        exit(EXIT_FAILURE);
    }
    // 获取共享内存ID
    if ((shmid = shmget(key, 1024, 0666 | IPC_CREAT)) == -1){
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }
    // 将共享内存附加到当前进程的地址空间
    shm = (char *)shmat(shmid, 0, 0);
    if (shm == (char *)(-1)){
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }
    
    // 获取信号量ID
    if ((semid = semget(key, 0, 0666 | IPC_CREAT)) == -1){
        perror("semget failed");
        exit(EXIT_FAILURE);
    }

    // 打印菜单
    printf("---------------------------------------\n");
    printf("--                IPC                --\n");
    printf("--  input w :write data send client  --\n");
    printf("--  input q :quit procedure          --\n");
    printf("---------------------------------------\n");

    // 主循环
    while (flag) {
        char c;
        printf("input:");
        scanf("%c", &c); // 读取用户输入
        switch (c) {
            // 写操作
            case 'w':
                getchar(); // 消耗掉scanf后的换行符
                p_handler(semid); // 执行P操作
                memset(str, 0, sizeof(str)); // 清空字符串
                printf("write:\n");
                // 读取用户输入的字符串
                fgets(str, sizeof(str), stdin); // 使用fgets代替gets以避免缓冲区溢出
                strcpy(shm, str); // 将用户输入的字符串复制到共享内存
                v_handler(semid); // 执行V操作

                // 发送消息给读者,告知有新数据写入共享内存
                readmsg.mtype = 888;
                readmsg.mtext = 'r';
                msgsnd(msgid, &readmsg, sizeof(struct msgbuf), 0);
                break;

            // 退出操作
            case 'q':
                printf("quit\n");
                // 清除输入缓冲区直到遇到换行符或文件结束符
                while ((c = getchar()) != '\n' && c != EOF);
                // 发送退出消息
                readmsg.mtype = 888;
                readmsg.mtext = 'q';
                msgsnd(msgid, &readmsg, sizeof(struct msgbuf), 0);
                flag = 0; // 设置退出标志
                break;

            // 输入错误处理
            default:
                printf("%c input error\n", c);
                // 清除输入缓冲区
                while ((c = getchar()) != '\n' && c != EOF);
                break;
        }
    }
    return 0;
}

运行结果:左侧为服务端,接收来自客户端的消息,右侧为客户端,发送指令然后传递消息。
在这里插入图片描述

参考文章:【Linux编程】进程间通信(IPC)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/703920.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决linux jenkins要求JDK版本与项目版本JDK不一致问题

背景–问题描述&#xff1a; 新入职公司&#xff0c;交接人说jenkins运行有问题&#xff0c;现在都是手动发布&#xff0c;具体原因让我自己看&#xff08;笑哭&#xff09;。我人都蒙了&#xff0c;测试环境都手动发布&#xff0c;那不是麻烦的要死&#xff01; 接手后&am…

【后端开发】服务开发场景之高可用(冗余设计,服务限流,降级熔断,超时重试,性能测试)

【后端开发】服务开发场景之高可用&#xff08;冗余设计&#xff0c;服务限流&#xff0c;降级熔断&#xff0c;超时重试&#xff0c;性能测试&#xff09; 文章目录 序&#xff1a;如何设计一个高可用的系统&#xff1f;可用性的判断指标是什么&#xff1f;哪些情况会导致系统…

人工智能(三)AI是怎么学习的

一、引言 通过之前的人工智能架构分析和Transformer模型的原理介绍&#xff0c;读者应该对人工智能有了一个初步的了解。 但是很多读者不是很想知道那么多软件方面的专业知识&#xff0c;通过大家的问题&#xff0c;大家关心的主要是三个方面&#xff1a; ai是怎么学习的&#…

数字政府与大模型

1. 什么是数字政府&#xff1f; 数字政府是指运用信息技术&#xff0c;如互联网、大数据、云计算等&#xff0c;来改革政府的服务提供方式、决策过程和内部管理&#xff0c;以提升效率、透明度和公众参与度的新型政府形态。 2. 大模型在数字政府中的作用是什么&#xff1f; …

htb_Blurry

端口扫描 8 按照教程注册安装clear ml 加载configuration的时候会报错 将json里的API&#xff0c;File Store的host都添加到/etc/hosts中 即可成功初始化 查找clear ml漏洞 发现一个cve-2024-24590 下面是一个利用脚本&#xff0c;但不能直接用 ClearML-vulnerability-e…

partially initialized module ‘charset_normalizer‘ has no attribute ‘md__mypyc‘

django项目运行报错&#xff1a; partially initialized module ‘charset_normalizer‘ has no attribute ‘md__mypyc‘…… 解决办法 pip install --force-reinstall charset-normalizer3.1.0

OpenCV 的模板匹配

OpenCV中的模板匹配 模板匹配&#xff08;Template Matching&#xff09;是计算机视觉中的一种技术&#xff0c;用于在大图像中找到与小图像&#xff08;模板&#xff09;相匹配的部分。OpenCV提供了多种模板匹配的方法&#xff0c;主要包括基于相关性和基于平方差的匹配方法。…

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】线性分类模型损失函数对比

本节均以二分类问题为例进行展开&#xff0c;统一定义类别标签 y ∈ { 1 , − 1 } y\in\{1,-1\} y∈{1,−1}&#xff0c;则分类正确时 y f ( x ; w ) > 0 yf(x;w)>0 yf(x;w)>0&#xff0c;且值越大越正确&#xff1b;错误时 y f ( x ; w ) < 0 yf(x;w)<0 yf(x;…

Anime Girls Pack

动漫女孩包 35个动画&#xff08;就地&#xff09;支持人形。 8情绪。 角色列表&#xff1a;原艾艾琪惠美子惠理文子星薰和子佳子奈子理子凛老师小樱老师津雨僵尸女孩01 下载&#xff1a;​​Unity资源商店链接资源下载链接 效果图&#xff1a;

Python 中浅拷贝(copy)和深拷贝(deepcopy)

1. 浅拷贝&#xff1a; 它创建一个新的对象&#xff0c;但对于原始对象内的子对象&#xff08;如列表中的嵌套列表&#xff09;&#xff0c;只是复制了引用。例如&#xff1a; import copy original_list [1, 2, 3] shallow_copied_list copy.copy(original_list) original_…

【stable diffusion】ComfyUI扩展安装以及点开后页面空白问题解决办法

扩展安装 虽然大家都推荐将扩展包直接放到extension文件夹的方式&#xff0c;但我还是推荐直接在sd webui的扩展处下载&#xff0c;酱紫比较好维护一点&#xff0c;我个人感觉。 按照上图顺序点击会出现”URLError: <urlopen error [Errno 11004] getaddrinfo failed>”…

排序

排序的概念及引用 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#…

采用创新的FPGA 器件来实现更经济且更高能效的大模型推理解决方案

作者&#xff1a;Bob Siller&#xff0c;Achronix半导体产品营销总监 摘要&#xff1a;本文根据完整的基准测试&#xff0c;将Achronix Semiconductor公司推出的Speedster7t FPGA与GPU解决方案进行比较&#xff0c;在运行同一个Llama2 70B参数模型时&#xff0c;该项基于FPGA的…

作业8:信息存储的层次与并行技术

作业8&#xff1a;信息存储的层次与并行技术 一. 单选题&#xff08;共7题&#xff0c;70分&#xff09; (单选题) 考虑为以下表达式生成代码 A&#xff1d;B&#xff0b;C &#xff1b; D&#xff1d;E&#xff0d;F &#xff1b; 在执行过程中不需要插入任何停顿周期就能够消…

【机器学习】支持向量机(个人笔记)

文章目录 SVM 分类器的误差函数分类误差函数距离误差函数C 参数 非线性边界的 SVM 分类器&#xff08;内核方法&#xff09;多项式内核径向基函数&#xff08;RBF&#xff09;内核 源代码文件请点击此处&#xff01; SVM 分类器的误差函数 SVM 使用两条平行线&#xff0c;使用…

探索Edge

目录 1.概述 1.1.什么是浏览器 1.2.浏览器的作用 2.Edge 2.1.什么是Edge 2.2.诞生背景 2.3.历史版本 2.4.作用 2.5.优缺点 2.5.1.优点 2.5.2.缺点 3.对比 3.1.和360浏览器的对比 3.2.和谷歌浏览器&#xff08;Chrome&#xff09;的对比 4.未来展望 5.总结 1.概…

构建稳定高效的消息传递中间件:消息队列系统的设计与实现

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 一、引言 二、设计目标 2.1、高可用性 1. 集群搭建 1.1 …

【ai】blender4.1 安装插件

开源软件,所以资料充足插件及配置 下载插件插件是python开发的 编辑中的偏好设置 点击选中 点击一键切换中文英文 切换主题 插件源码

R语言统计分析——图形文本、自定义坐标轴和图例

参考资料&#xff1a;R语言实战【第2版】 我们可以在图形上添加标题&#xff08;main&#xff09;、副标题&#xff08;sub&#xff09;、坐标轴标签&#xff08;xlab、ylab&#xff09;并指定标轴范围&#xff08;xlim、ylim&#xff09;。 # 录入数据 dose<-c(20,30,40,4…

Go API

Go语言提供了大量的标准库&#xff0c;因此 google 公司也为这些标准库提供了相应的API文档&#xff0c;用于告诉开发者如何使用这些标准库&#xff0c;以及标准库包含的方法。官方位置&#xff1a;https://golang.org Golang中文网在线标准库文档: https://studygolang.com/p…