当前位置: 首页 > news >正文

【Linux】进程间通信(三)——共享内存和消息队列

目录

一、共享内存

1、概念:

2、共享内存的使用

创建共享内存:shmget

建立映射:shmat;s可以访问内存空间

断开映射:shmdt;s不可以访问内存空间

删除共享内存:shmctl

3、共享内存操作

操作步骤:

具体实现:

结果:

4、优缺点:

二、消息队列

1、概念

2、消息队列的使用

 创建消息队列:msgget

添加消息队列:msgsnd

获取消息:msgrcv

删除消息队列:msgctl

注意事项

3、消息队列的操作

具体实现

结果

4、优缺点

消息队列的优点

消息队列的缺点

一、共享内存

    1、概念:

    共享内存是一种进程间通信(IPC)机制,允许多个进程访问同一块物理内存区域,从而实现高效数据共享。由于无需数据拷贝,它是速度最快的IPC方式之一,但需要同步机制(如信号量、互斥锁)避免竞态条件。头文件为——<sys/sem.h> 


    2、共享内存的使用

    创建共享内存:shmget

    使用shmget()函数,通过唯一键值(key_t)创建或获取共享内存标识符。

    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//创建共享内存

    建立映射:shmat;s可以访问内存空间

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

    char* s=(char*)shmat(shmid,NULL,0);//建立映射,s可以访问内存空间

    断开映射:shmdt;s不可以访问内存空间

    使用shmdt()解除映射,但内存段仍存在直至显式删除。

    shmdt(s);//断开映射,s不可以访问内存空间

    删除共享内存:shmctl

    shmctl()用于删除(IPC_RMID)或查询状态。

     shmctl(shmid,IPC_RMID,NULL);//删除共享内存

    3、共享内存操作

    操作步骤:

    • 引入信号量
    • p操作
    • v操作
    • 销毁
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/sem.h>enum INDEX{SEM1 = 0,SEM2};union semun 
    {int val;
    };void sem_init();//初始化
    void sem_p(int index);//p
    void sem_v(int index);//v
    void sem_destroy();

    具体实现:

    a.c文件

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/shm.h>
    #include"sem.h"int main(){int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//创建共享内存if(-1==shmid){exit(1);}char* s=(char*)shmat(shmid,NULL,0);//建立映射,s可以访问内存空间if(s==(char*)-1){exit(1);}sem_init();while(1){printf("input\n");char buff[128]={0};fgets(buff,128,stdin);//从键盘获取数据sem_p(SEM1);strcpy(s,buff);//写入内存中sem_v(SEM2);if( strncmp(buff,"end",3) == 0){//比较字符串的前三个字符endbreak;}}//strcpy(s,"hello");//向共享内存写入helloshmdt(s);//断开映射,s不可以访问内存空间return 0;
    }

    b.c文件

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/shm.h>
    #include"sem.h"int main(){int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);if(-1==shmid){exit(1);}char* s=(char*)shmat(shmid,NULL,0);//建立映射,s可以访问内存空间if(s==(char*)-1){exit(1);}sem_init();while(1){sem_p(SEM2);if(strncmp(s,"end",3)==0){break;}printf("s=%s\n",s);sem_v(SEM1);sleep(1);}shmdt(s);//断开映射,s不可以访问内存空间shmctl(shmid,IPC_RMID,NULL);//删除共享内存sem_destroy();//销毁exit (0);}
    

    sem.c文件

    #include "sem.h"static int semid = -1;
    #define SEM_NUM 2void sem_init()
    {semid = semget((key_t)1234,SEM_NUM,IPC_CREAT|IPC_EXCL|0600);//尝试全新创建if( -1 == semid )//新建失败,说明已存在{semid = semget((key_t)1234,SEM_NUM,0600);if( -1 == semid ){printf("semget err\n");}}else{int arr[SEM_NUM] = {1,0};for(int i = 0; i < SEM_NUM; i++){union semun a;a.val = arr[i];if( semctl(semid,i,SETVAL,a) == -1){printf("semctl setval err\n");}}}
    }
    void sem_p(int index){struct sembuf buf;buf.sem_num = index;buf.sem_op = -1;//pbuf.sem_flg = SEM_UNDO;//进程异常结束,系统会自动归还信号量if( semop(semid,&buf,1) == -1){printf("p err\n");}
    }
    void sem_v(int index)
    {struct sembuf buf;buf.sem_num = index;buf.sem_op = 1;//vbuf.sem_flg = SEM_UNDO;if( semop(semid,&buf,1) == -1){printf("v err\n");}
    }
    void sem_destroy()
    {if( semctl(semid,0,IPC_RMID) == -1){printf("sem destroy err\n");}
    }

    结果:

    4、优缺点:

    优点

    • 高性能:直接内存访问,无数据拷贝。
    • 适合大规模数据交换。

    缺点

    • 需手动同步,编程复杂度高。
    • 系统崩溃可能导致数据不一致。

    二、消息队列

    1、概念

    消息队列是一种异步通信机制,用于解耦生产者和消费者。生产者将消息发送到队列,消费者从队列中获取并处理消息,双方无需直接交互。这种模式适用于分布式系统、流量削峰、异步任务等场景。头文件为——<sys/msg.h> 

    2、消息队列的使用

     创建消息队列:msgget

    在Linux系统中,消息队列通常通过msgget系统调用创建。该函数需要指定一个键值(key)和一组标志(flags)。键值可以是IPC_PRIVATE或通过ftok生成的唯一键值。标志通常包括权限位(如0666)和IPC_CREAT

    int msgid=msgget((key_t)1234,IPC_CREAT|0600);

    添加消息队列:msgsnd

    消息通过msgsnd函数发送到队列。消息需要包含一个长整型的消息类型和实际数据。结构体需以long mtype开头,后面跟随数据内容。

    msgsnd(msgid,(void*)&dt,128,0);

    获取消息:msgrcv

    消息通过msgrcv函数从队列中接收。接收时需要指定消息类型(msgtyp),若为0则接收队列中第一条消息。

    //msgid消息队列id,dt接收数据存放的地点,128数据部分大小,0不区分类型,0标志位if(msgrcv(msgid,(void*)&dt,128,1,0)==-1){//接受消息类型为0,代表不区分消息类型exit(1);}

    删除消息队列:msgctl

    使用msgctl函数并指定IPC_RMID命令可以删除消息队列。

    msgctl(msgid,IPC_RMID,NULL);

    注意事项

    • 消息队列是持久的,即使进程结束,队列仍会保留直到显式删除。
    • 消息大小和队列容量受系统限制,可通过/proc/sys/kernel/msgmaxmsgmnb查看。
    • 权限设置需合理,避免未授权访问。

    3、消息队列的操作

    具体实现

    main.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/msg.h>struct mess{long type;char buff[128];
    };
    int main(){int msgid=msgget((key_t)1234,IPC_CREAT|0600);if(msgid==-1){exit(1);}struct mess dt;dt.type=1;strcpy(dt.buff,"hello");msgsnd(msgid,(void*)&dt,128,0);
    }

    tset.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/msg.h>struct mess{long type;char buff[128];
    };
    int main(){int msgid=msgget((key_t)1234,IPC_CREAT|0600);if(msgid==-1){exit(1);}struct mess dt;//msgid消息队列id,dt接收数据存放的地点,128数据部分大小,0不区分类型,0标志位if(msgrcv(msgid,(void*)&dt,128,1,0)==-1){//接受消息类型为0,代表不区分消息类型exit(1);}printf("read=%s\n",dt.buff);//msgctl(msgid,IPC_RMID,NULL);
    }

    结果

    4、优缺点

    消息队列的优点

    异步处理
    消息队列允许发送者和接收者异步工作,发送者将消息放入队列后无需等待接收者处理,提高系统响应速度和解耦性。

    削峰填谷
    在高并发场景下,消息队列能缓冲瞬时流量高峰,避免系统过载。后续由消费者逐步处理积压消息,平滑资源使用。

    解耦系统组件
    生产者和消费者通过队列通信,无需直接耦合。系统组件可独立扩展或修改,降低维护复杂度。

    可靠性保障
    多数消息队列提供持久化、重试和死信队列机制,确保消息不丢失。即使消费者暂时不可用,数据仍可恢复。

    顺序性保证
    部分队列(如Kafka分区)支持消息顺序处理,适用于需要严格时序的业务场景。

    跨语言/平台通信
    基于标准化协议(如AMQP),不同技术栈的系统可通过消息队列交互,无需依赖特定接口格式。


    消息队列的缺点

    系统复杂度增加
    引入消息队列需额外维护中间件,部署监控、容灾等设施,增加架构复杂性和运维成本。

    消息延迟
    异步处理可能导致消息消费滞后,不适用于实时性要求极高的场景(如毫秒级响应)。

    一致性问题
    若消息消费失败或重复消费,可能引发数据不一致,需额外设计幂等性处理或事务补偿机制。

    性能瓶颈
    队列本身可能成为性能瓶颈,尤其在消息堆积时。需合理配置分区、副本等参数以优化吞吐量。

    资源消耗
    消息队列服务通常占用较高内存和存储资源,尤其在消息持久化场景下,硬件成本可能显著增加。

    调试难度
    分布式环境下,消息链路追踪和问题排查较复杂,需依赖日志、链路追踪等工具辅助。

    http://www.lryc.cn/news/592607.html

    相关文章:

  • 特种作业操作证(制冷空调)的考试科目有哪些?
  • Spring AI开发智能客服(Tool calling)
  • 第七章 愿景09 海波龙的坑
  • 链表算法之【链表的中间节点】
  • MSTP+VRRP+DHCP配置实验(ensp)
  • 医疗人工智能的心电图分析:创新技术与临床应用
  • 多组件Canvas ID冲突解决方案
  • Pythonday17
  • 深入理解进程地址空间:虚拟内存与进程独立性
  • 2-大语言模型—理论基础:详解Transformer架构的实现(2)
  • 专题 原型与继承完全指南
  • QT聊天项目DAY15
  • 更适合后端宝宝的前端三件套之HTML
  • GEV/POT/Markov/点过程/贝叶斯极值全解析;基于R语言的极值统计学
  • 设计模式五:桥模式(Bridge Pattern)
  • 关于在VScode中使用git的一些步骤常用命令及其常见问题:
  • Redis工具类
  • RHCE第二次作业
  • MyBatis:配置文件完成增删改查_添加
  • Java 核心工具类 API 详解(一):从 Math 到 Runtime 的实用指南
  • 谷歌浏览器Chrome的多用户配置文件功能
  • 简单易懂,基本地址变换机构
  • 高防IP能够防御CC攻击吗?它具备哪些显著优势?
  • 【easytokenizer】高性能文本 Tokenizer库 | 源码详解与编译安装
  • Java中类加载器及双亲委派机制原理
  • 2023 年 3 月青少年软编等考 C 语言八级真题解析
  • Windows8.1安装哪个版本的vscode?
  • 基于华为openEuler系统安装DailyNotes个人笔记管理工具
  • HTML常见标签
  • 关于Mysql开启慢查询日志报错:13 - Permission denied的解决方案