进程间通信方式(共享内存、信号灯集、消息队列)

news2024/9/24 3:22:47

共享内存

特点

1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程

将其映射到自己的私有地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

3) 由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

使用步骤

(1) 创建key值: ftok

(2) 创建或打开共享内存: shmget

(3) 映射共享内存到用户空间: shmat

(4) 撤销映射: shmdt

(5) 删除共享内存: shmctl

进程间通信:

命令

ipcs -m: 查看系统中的共享内存

ipcrm -m shmid:删除共享内存

ps: 可能不能直接删除掉还存在进程使用的共享内存,这时候可以用ps -ef对进程进行查看,kill掉多余的进程后,再使用ipcs查看。 

练习:两个进程实现通信,一个进程循环从终端输入,另一个进程循环打印,当输入quit时结束

这两个标志在两个进程里,是不共享的,所以为了共享标志位可以和buf封装到一个结构体里作为共享内存。

struct msg

{

int flag;

char buf[32];

};

头文件

typedef struct shared
{
    int flag;
    char buf[32];
} shared_t, *shared_p;

输入端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "shared.h"
int main(int argc, char const *argv[])
{
    key_t key;
    key = ftok("bus.c", 'a');
    if (key < 0)
    {
        perror("key err");
        return -1;
    }
    printf("key:%#x\n", key);

    int shmid = shmget(key, sizeof(shared_t), IPC_CREAT | IPC_EXCL | 0777);
    if (shmid <= 0)
    {
        if (errno == EEXIST)
        {
            shmid = shmget(key, sizeof(shared_t), 0777);
        }
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);

    shared_p p = (shared_p)shmat(shmid, NULL, 0);
    if (p == (shared_p)-1)
    {
        perror("shmat err");
        return -1;
    }
    p->flag = 0;
    while (1)
    {
        if (p->flag == 0)
        {
            scanf("%s", p->buf);
            if (strcmp(p->buf, "quit") == 0)
                break;
            p->flag++;
        }
    }

    shmdt(p);

    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

输出端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "shared.h"
int main(int argc, char const *argv[])
{
    key_t key;
    key = ftok("bus.c", 'a');
    if (key < 0)
    {
        perror("key err");
        return -1;
    }
    printf("key:%#x\n", key);

    int shmid = shmget(key, sizeof(shared_t), IPC_CREAT | IPC_EXCL | 0777);
    if (shmid <= 0)
    {
        if (errno == EEXIST)
        {
            shmid = shmget(key, sizeof(shared_t), 0777);
        }
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid:%d\n", shmid);

    shared_p p = (shared_p)shmat(shmid, NULL, 0);
    if (p == (shared_p)-1)
    {
        perror("shmat err");
        return -1;
    }

    while (1)
    {
        sleep(1);
        if (strcmp(p->buf, "quit") == 0)
            exit(0);
        if (p->flag == 1)
        {
            printf("%s\n", p->buf);
            p->flag--;
        }
    }

    shmdt(p);

    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

信号灯集

特点

信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;

而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)

System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。

通过信号灯集实现共享内存的同步操作

使用步骤

(1) 创建键值:ftok

(2) 创建或打开信号灯集:semget

(3) 初始化信号灯:semctl

(4) pv操作:semop

(5) 删除信号灯集:semctl

命令

ipcs -s:查看信号灯集

ipcrm -s semid:删除信号灯集

注意:有时候可能会创建失败,或者semid为0,所以用命令看看,删了重新创建就可以了。

练习:两个进程实现通信,一个进程循环从终端输入,另一个进程循环打印,把信号灯集加到共享内存实现同步,输入输出quit结束

头文件

union semun
{
    int val;
};
key_t keyfun(char buf[32], char ch);
int shmcreate(key_t key);
int semcreate(key_t key, int num);
void seminit(int semid, int val, int num);
void sempv(int semid, int num, int op);

功能函数

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"

key_t keyfun(char buf[32], char ch)
{
    key_t key;
    key = ftok(buf, ch);
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key:%#x\n", key);
    return key;
}

int shmcreate(key_t key)
{
    int shmid = shmget(key, sizeof(str), IPC_CREAT | IPC_EXCL | 0777);
    if (shmid <= 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, sizeof(str), 0777);
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid: %d\n", shmid);
    return shmid;
}

int semcreate(key_t key, int num)
{
    int semid = semget(key, num, IPC_CREAT | IPC_EXCL | 0777);
    if (semid <= 0)
    {
        if (errno == EEXIST)
        {
            semid = semget(key, num, 0777);
        }
        else
        {
            perror("semget err");
            return -1;
        }
    }
    else
    {
        seminit(semid, 0, 0);
        seminit(semid, 1, 1);
    }
    printf("semid:%d\n", semid);
    return semid;
}

void seminit(int semid, int num, int val)
{
    union semun mysem;
    mysem.val = val;
    semctl(semid, num, SETVAL, mysem);
}

void sempv(int semid, int num, int op)
{
    struct sembuf buf; // sembuf结构体人家写好的直接拿来用就可以
    // PV操作
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;       // 阻塞
    semop(semid, &buf, 1); // 对num号灯进行op操作申请资源
}

输入端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"
int main(int argc, char const *argv[])
{
    key_t key = keyfun("sem.h", 'a');
    int semid = semcreate(key, 2);
    int shmid = shmcreate(key);

    // 映射共享内存
    char *p = (char *)shmat(shmid, NULL, 0);
    if (p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    while (1)
    {
        sempv(semid, 1, -1);
        scanf("%s", p);
        sempv(semid, 0, 1);
        if (strcmp(p, "quit") == 0)
            break;
    }
    printf("0:%d\n", semctl(semid, 0, GETVAL));
    printf("1:%d\n", semctl(semid, 1, GETVAL));
    shmdt(p);
    // semctl(semid, 0, IPC_RMID);
    return 0;
}

输出端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"
int main(int argc, char const *argv[])
{
    key_t key = keyfun("sem.h", 'a');
    int semid = semcreate(key, 2);
    int shmid = shmcreate(key);

    // 映射共享内存
    char *p = (char *)shmat(shmid, NULL, 0);
    if (p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }
    while (1)
    {
        sempv(semid, 0, -1);
        if (strcmp(p, "quit") == 0)
            break;
        printf("%s\n", p);
        sempv(semid, 1, 1);
    }
    printf("0:%d\n", semctl(semid, 0, GETVAL));
    printf("1:%d\n", semctl(semid, 1, GETVAL));
    shmdt(p);
    // semctl(semid, 0, IPC_RMID);
    return 0;
}

消息队列

特点

消息队列是IPC对象(活动在内核级别的一种进程间通信的工具)的一种

一个消息队列由一个标识符 (即队列ID)来标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等

消息队列可以按照类型(自己设一个值作为类型)来发送/接收消息

使用步骤

(1) 创建key值:ftok

(2) 创建或打开消息队列:msgget

(3) 添加消息:按照消息的类型把消息添加到已经打开的消息队列末尾msgsnd

(4) 读取消息:可以按照消息类型把消息从消息队列中取走msgrcv

(5) 删除消息队列:msgctl

命令

ipcs -q:查看消息队列

ipcrm -q msgid:删除消息队列

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

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

相关文章

1.第二阶段x86游戏实战2-前言

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本次会有100章左右&#xff0c;会从0基础开始&#xff0c;内容有找游戏中的数据、分析游戏中的数据&…

C++ | Leetcode C++题解之第389题找不同

题目&#xff1a; 题解&#xff1a; class Solution { public:char findTheDifference(string s, string t) {int ret 0;for (char ch: s) {ret ^ ch;}for (char ch: t) {ret ^ ch;}return ret;} };

REAL-FAKE: EFFECTIVE TRAINING DATA SYNTHESISTHROUGH DISTRIBUTION MATCHING 论文学习

这篇文章主要讲的是生成数据在模型训练中的作用&#xff0c;对于接下来要研究的生成多模态数据具有重要的作用。 文章摘要首先讲生成数据很重要&#xff0c;但在训练高级的模型的时候效果不好。论文主要研究的是这背后的原理并且证明了生成数据的作用。 介绍部分&#xff0c;…

在社交物联网中使用MQTT协议和Hardy Wall算法实现有效的多播通信

这篇论文的标题是《EFFECTIVE MULTICAST COMMUNICATION USING MQTT PROTOCOL AND HARDY WALL ALGORITHM IN SIOT》&#xff0c;作者是 S.Jayasri 和 Dr. R.Parameswari&#xff0c;发表在《International Journal of Applied Engineering & Technology》2023年9月的第5卷第…

kubeadm方式升级k8s集群

一、注意事项 升级前最好备份所有组件及数据&#xff0c;例如etcd 不要跨两个大版本进行升级&#xff0c;可能会存在版本bug&#xff0c;如&#xff1a; 1.19.4–>1.20.4 可以 1.19.4–>1.21.4 不可以 跨多个版本的可以逐个版本进行升级。 二、查看当前版本 [rootk8s…

Solidity初体验

一、概念知识 什么是智能合约&#xff1f; 智能合约是仅在满足特定条件时才在区块链上部署和执行的功能&#xff0c;无需任何第三方参与。 由于智能合约本质上是不可变的和分布式的&#xff0c;因此它们在编写和部署后无法修改或更新。此外&#xff0c;分布式的意义在于任何…

上证50ETF期权交易策略有哪些?期权交易时要注意什么?

今天带你了解上证50ETF期权交易策略有哪些&#xff1f;期权交易时要注意什么&#xff1f;上证50ETF期权是一种以华夏50etf基金为标的物的金融衍生品&#xff0c;它允许投资者通过买卖期权合约来对冲风险或进行投机。 期权趋势型策略 趋势型的策略就是我们通常说的追涨杀跌&am…

git代码托管仓库02(分支与冲突)

分支 所有版本控制系统都以某种形式支持分支。使用分支意味着可以把自己的工作从开发主线上分离来进行重大的bug修改&#xff0c;开发新的功能&#xff0c;以免影响主线开发 该master就是分支 查看分支&#xff1a;git branch 添加分支&#xff1a; git branch 分支名 可以看…

C++笔记17•数据结构:二叉搜索树(K模型/KV模型实现)•

二叉搜索树 1.二叉搜索树 1. 二叉搜索树的查找 a 、从根开始比较&#xff0c;查找&#xff0c;比根大则往右边走查找&#xff0c;比根小则往左边走查找。 b 、最多查找高度次&#xff0c;走到到空&#xff0c;还没找到&#xff0c;这个值不存在。2. 二叉搜索树的插入 插入的具…

【图解版】Likes Vs Dislikes——C语言提高题【7 kyu】

一、原题 链接&#xff1a;Training on Likes Vs Dislikes | Codewars YouTube had a like and a dislike button, which allowed users to express their opinions about particular content. It was set up in such a way that you cannot like and dislike a video at the…

集成电路学习:什么是PWM脉冲宽度调制

一、PWM&#xff1a;脉冲宽度调制 PWM&#xff0c;全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;是一种通过调整脉冲信号的宽度来控制电路输出的技术。在PWM中&#xff0c;信号的输出由一系列等幅值的脉冲组成&#xff0c;这些脉冲的宽度根据…

h5适配iOS——window.open失效

在H5开发中&#xff0c;打开一个新的标签页一般使用window.open。安卓中没啥限制&#xff0c;但是iOS的Safari出于安全限制&#xff0c;想使用该方法打开新的网页&#xff0c;必须得有用户手动触发&#xff0c;用代码自动打开是会失效的。 也就是说&#xff0c;window.open必须…

VS配置QGIS二次开发(VS2019+QT5+QGIS3.14)

1.1 QGIS软件下载 QGIS软件是基于QT5开发的GIS系统&#xff0c;其易用性及其高效性使其已经成为当下极具竞争力的GIS系统&#xff0c;其占有率足以与Esri的ArcGIS等主流GIS软件分庭抗礼。QGIS软件因其开源特性也使得其广受广大GIS开发者的青睐。 在本次《地理信息系统设计与开…

风趣图解LLMs RAG的15种设计模式-第二课

全部用的是Midjourney连续性一致性绘图画成

前端报文加密

前端加密功能 前端提供简单的AES对称加密算法&#xff0c;注意key 和后端网关配置相同&#xff0c;这里打包混淆后&#xff0c;相对安全。 &#xff08;lun-ui\src\store\modules\user.js、base-gateway-dev.yml&#xff09; 后端解密功能 使用hutool提供的工具类进行解密pub…

鸿蒙开发基础知识 第二篇【页面布局】

鸿蒙开发基础知识 第二篇 1. 两端对齐 demo 2.交叉轴对齐方式 demo 3.列表项布局 demo 4.自适应伸缩布局 demo 自行练习 5.弹性布局 flex 换行布局 demo 案例 更多鸿蒙技能知识与案例 我已经整理到下面了 ↓↓↓ 快去看那看吧&#xff01; 点击下方↓↓↓↓↓↓↓…

【C++】类和对象(三)再探构造函数|static成员函数|友元函数|内部类|匿名对象|对象拷贝时的编译优化

欢迎来到HarperLee的学习笔记&#xff01; 一、再探构造函数 初始化列表&#xff1a;构造函数初始化的第二种方式&#xff08;第一种是使用函数体内赋值&#xff09;。使用方式&#xff1a;以一个冒号:开始&#xff0c;用逗号,分隔数据成员列表&#xff0c;每个成员变量后面跟…

Linux 命令行/bash脚本 批量创建文件

目录 一. 命令行1.1 需求1.2 代码 二. bash脚本2.1 需求2.2 前置知识2.3 无进度条版本2.3.1 知识点2.3.2 代码2.3.3 效果 2.4 有进度条版本2.4.1 代码2.4.2 效果 一. 命令行 1.1 需求 在当目录下生成指定年份的文件&#xff0c;要求从生成1月到12月&#xff0c;每个月份的文件…

如何使用 update-alternatives 切换软件版本 ?

Debian 中的 update-alternatives 命令是系统上多版本软件管理的利器。它允许您轻松地在同一程序的不同版本之间切换。假设您在单个系统上安装了多个 Java 版本或 Python 版本&#xff0c;在 update-alternatives 的帮助下&#xff0c;您可以将程序的任何版本设置为系统的默认版…

SAP MM模块与FI模块集成之科目配置

1. 定义评估范围 OMWD 评估范围设置在工厂层&#xff0c;那么系统自动建立和工厂具有相同ID的评估范围 IMG&#xff1a;物料管理>评估和科目设置>科目确定>无向导的科目确定>将评估范围群集分组 提示&#xff1a;评估层级——评估分组代码——评估范围。 2. OBYC…