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

news2024/11/8 5:40:05

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)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】新网络环境docker实战:AI智能体平台DIFY部署与升级

目录 一、引言 二、Dify在dockerhub被禁用后&#xff0c;如何部署、升级 2.1 网络及硬件条件 2.2 docker部署、升级方案 三、总结 一、引言 关于dify&#xff0c;之前力推过&#xff0c;大家可以跳转 AI智能体研发之路-工程篇&#xff08;二&#xff09;&#xff1a;Dify…

Spring Boot整合hibernate-validator实现数据校验

文章目录 概念基本概念常用校验注解 前置内容整合Hibernate Validator快速入门优雅处理参数校验异常其余注解校验自定义校验注解 参考来源 概念 基本概念 Hibernate Validator 是一个参数校验框架&#xff0c;可以非常方便地帮助我们校验应用程序的入参&#xff0c;实现了参数…

App UI 风格展现非凡创意

App UI 风格展现非凡创意

基于 Python 解析 XML 文件并将数据存储到 MongoDB 数据库

1. 问题背景 在软件开发中&#xff0c;我们经常需要处理各种格式的数据。XML 是一种常用的数据交换格式&#xff0c;它可以存储和传输结构化数据。很多网站会提供 XML 格式的数据接口&#xff0c;以便其他系统可以方便地获取数据。 我们有这样一个需求&#xff1a;我们需要从…

Maven认识与学习

1. Maven介绍 1.2 初识Maven 1.2.1 什么是Maven Maven是Apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具。 官网&#xff1a;Maven – Welcome to Apache Maven Apache 软件基金会&#xff0c;成立于1999年7月&#xff0c;是目前世界上最大的最受…

13600KF+3060Ti,虚拟机安装macOS 14,2024年6月

距离上次装macOS虚拟机已经有一段时间了&#xff0c;macOS系统现在大版本升级的速度也是越来越快了&#xff0c;由于Office只支持最新三个版本的macOS&#xff0c;所以现在保底也得安装macOS 12了&#xff0c;我这次是用macOS 14做实验&#xff0c;13和12的安装方式和macOS 14一…

“人事助理转产品经理”历险记

​好久没写就业喜报了 去年太忙&#xff0c;年后了&#xff0c;必须给大家把同学们就业的情况梳理一下分享出来。希望对大家有所帮助。 同学档案 原岗位&#xff1a;HR 地点&#xff1a;西安 工作年限&#xff1a;2年 转岗级别&#xff1a;中级产品经理 转岗工资&#xff1…

【Android】Android系统性学习——Android系统架构

前言 部分内容参考《Android进阶解密》 – 刘望舒 1. Android版本 官方链接&#xff1a;https://developer.android.com/studio/releases/platforms 里面有各个版本的官方文档&#xff0c;有些新功能的用法在这里面。 现在做安卓11&#xff0c;有时候需要向下兼容 2. AOSP …

做了2年前端,盘点前端技术栈!大佬轻喷~

前言 自己写了快两年前端&#xff0c;但是大致总结一下哈哈哈哈我觉得这个话题蛮有意思的&#xff0c;可以看看大家的技术广度&#xff0c;可以进行分享和学习以及讨论所以这里说一下我对我的前端技术&#xff0c;做一下盘点和总结因为我的开发年限有限&#xff0c;所以我觉得…

CSS 字体颜色渐变

CSS 字体颜色渐变 css 代码: 注意&#xff1a;background: linear-gradient&#xff08;属性&#xff09;&#xff0c;属性可以调整方向 例如&#xff1a;to bottom 上下结构&#xff0c;to right 左右结构font-family: DIN, DIN;font-weight: normal;font-size: 22px;color:…

openlayers 绘图功能,编辑多边形,select,snap组件的使用(六)

本篇介绍一下vue3-openlayers的select&#xff0c;snap的使用 1 需求 点击开始绘制按钮开始绘制多边形&#xff0c;可以连续绘制多个多边形点击撤销上步按钮&#xff0c;撤销上一步绘制点绘制多个多边形&#xff08;或编辑多边形时&#xff09;&#xff0c;鼠标靠近之前的已绘…

Vue3【二十二】Vue 路由模式的嵌套路由和用query给组件的RouterLink传参

Vue3【二十二】Vue 路由模式的嵌套路由和用query给组件传参 Vue3【二十二】Vue 路由模式的嵌套路由和用query给组件传参 RouterLink 的两种传参方法 RouterView 案例截图 目录结构 代码 index.ts // 创建一个路由器&#xff0c;并暴漏出去// 第一步&#xff1a;引入createRou…

0613#111. 构造二阶行列式

时间限制&#xff1a;1.000S 空间限制&#xff1a;256MB 题目描述 小欧希望你构造一个二阶行列式&#xff0c;满足行列式中每个数均为不超过 20 的正整数&#xff0c;且行列式的值恰好等于x。你能帮帮她吗? 输入描述 一个正整数x。-1000 < x < 1000 输出描述 如果…

商家转账到零钱最全面攻略:申请、使用、注意事项等详解

一、微信商家转账到零钱功能概述 微信支付作为国内最大社交软件的增值服务&#xff0c;在商业活动中广泛使用。其开发的营销功能“商家转账到零钱”则允许商家直接将资金转入用户的微信钱包&#xff0c;操作简便快捷。本文将详细探讨此功能的使用条件、操作步骤以及解答一些常…

如何通过 6 种方法从 iPhone 恢复已删除的文件

想知道如何从 iPhone 恢复已删除的文件吗&#xff1f;本文将指导您如何从 iPhone 恢复数据&#xff0c;无论您是否有 iTunes/iCloud 备份。 iPhone 上已删除的文件去哪儿了&#xff1f; 许多 iPhone 用户抱怨他们经常丢失 iPhone 上的一些重要文件。由于意外删除、iOS 更新失败…

遇到JSON文件就头大?掌握Python这几种方法,让你轻松应对

目录 1、标准库json模块 📄 1.1 json.load()函数介绍 1.2 json.loads()处理字符串 1.3 使用json.dump()写入JSON 1.4 json.dumps()美化输出 1.4 错误处理与编码问题 1.5 高效读取大文件技巧 2、第三方库simplejson加持 🔧 2.1 安装与导入simplejson 2.2 性能优势与…

韩国版AlphaFold?深度学习模型AlphaPPIMd:用于蛋白质-蛋白质复合物构象集合探索

在生命的舞台上&#xff0c;蛋白质扮演着不可或缺的角色。它们是生物体中最为活跃的分子&#xff0c;参与细胞的构建、修复、能量转换、信号传递以及无数关键的生物学功能。同时&#xff0c;蛋白质的结构与其功能密切相关&#xff0c;而它们的功能又通过与蛋白质、多肽、核苷酸…

node调试

vscode安装插件&#xff1a;JavaScript Debugger (Nightly) 点击后生成一个launch.json文件 打断点&#xff0c;并发送一个请求来执行代码到断点处 按右上的向下箭头&#xff0c;进入源码&#xff0c;进行查看&#xff0c;左边查看变量等值

护眼台灯攻略:护眼台灯真的有用吗?

当前&#xff0c;近视问题在人群中愈发普遍&#xff0c;据2024年的统计数据显示&#xff0c;我国儿童青少年的总体近视率已高达52.7%。近视的人越来越多&#xff0c;近视背后还潜藏着视网膜脱离、白内障、开角型青光眼等眼部疾病&#xff0c;严重的情况甚至可能引发失明。长时间…

windows 环境下使用git命令导出差异化文件及目录

一、找出差异化的版本&#xff08;再此使用idea的show history&#xff09; 找到两个提交记录的id 分别为&#xff1a; 二、使用git bash执行命令&#xff08;主要使用 tar命令压缩文件&#xff09; 输出结果&#xff1a;