opencv 打开图片后,cv::mat存入共享内存的代码,实现消费者与生产者模型。XSI信号量和POSIX 信号量

文章目录

  • 基于 sys 系统信号量(XSI信号量)
    • 常用api
    • 参考
  • 基于 POSIX 信号量
    • 有名信号量
      • 常用 api
    • 无名信号量
      • 常用 api
    • 参考
  • 实践-基于POSIX有名信号量
    • 生产者消费者模型任务说明
      • 同步关系
      • 互斥关系
    • 设置一个互斥信号量,实现对共享内存的互斥访问
    • 设置两个信号量,用于标记资源的数目,实现进程间的两个同步关系
      • 发送
      • 接受
    • 参考的文心一言的伪代码
  • 代码分享
  • 下一步
    • 1、如何设置队列?
    • 2、使用STL 容器在共享内存怎么搞?
    • 3、消息队列,例如POSIX 消息队列

基于 sys 系统信号量(XSI信号量)

Linux内核提供了一些系统调用函数来创建和操作信号量,这些函数都定义在<sys/sem.h>头文件中。

XSI信号量相对于POSIX信号量要复杂很多,因为其以下特性:

(1) 信号量并非单个非负值,而是一个或多个信号量的集合。创建信号量时,要指定其中信号数量;

(2) 创建信号量(semget)和初始化信号量(semctl)相互独立,不能原子操作;

(3) XSI信号量不会在进程退出后自动销毁(所有XSI IPC都有这个毛病,包括消息队列,共享存储,信号量)。

每个信号量集合含有一个semid_ds结构体

struct semid_ds
{
struct ipc_perm sem_perm;
unsigned short sem_nsems; //信号数量
time_t sem_otime;
time_t sem_ctime;
//可能还有其他成员
}

常用api

函数名:semget
nsems表示信号集合的信号数量,如果创建新集合,指定nsems;如果引用现有集合,nsems=0
*/
int semget(key_t key,int nsems,int flag); /*
函数名:semctl
设置选项,最后一个union参数可选,根据cmd的命令
cmd命令:
IPC_STAT:取semid_ds结构体,存于arg.buf中
IPC_SET:按照arg.buf中的semid_ds结构体,设置sem_perm.uid,sem_perm.gid,sem_perm.mode
IPC_RMID:删除信号量集合,跟共享存储的IPC_RMID不同,此处立即删除,其他还在引用的进程再次使用该信号量集合时报错EIDRM
GETVAL:获取semnum的semval值
SETVAL:设置成员semnum的semval
GETPID:获取成员semnum的sempid
GETNCNT:获取semnum的semcnt
GETALL:获取所有信号量的semval,保存在arg.array中
SETALL:将所有信号量的semval设置为对应的arg.array值
*/
int semctl(int semid,int semnum,int cmd,.../*union semun arg*/);
union semun
{int val; //for SETVALstruct semid_ds* buf; //for IPC_STAT and IPC_SETunsigned short *array //for SETALL and GETALL
}
/*
函数名:semop(自动执行信号量集合上的操作数组)
semoparray:sembuf的数组
nops:semoparray中元素个数
*/
int semop(int semid,struct sembuf semoparray[],size_t nops); //sem_buf定义如下:
struct sem_buf
{usigned short sem_num; //member int set,即信号量集合中信号的序号short sem_op; //operationsshort sem_flag;  //IPC_NOWAIT,SEM_UNDO
};

参考

https://blog.csdn.net/BrilliantAntonio/article/details/120606129
https://zhuanlan.zhihu.com/p/642216249
https://zhuanlan.zhihu.com/p/621670727

基于 POSIX 信号量

基于的头文件是#include <semphore.h>

在 POSIX标准中,信号量分两种,一种是无名信号量,一种是有名信号量。 无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。 有名信号量和无名信号量的差异在于创建和销毁的形式上,但是其他工作一样,无名信号量则直接保存在内存中, 而有名信号量则要求创建一个文件。

抽象的来讲,信号量中存在一个非负整数,所有获取它的进程/线程都会将该整数减一, 当该整数值为零时,所有试图获取它的进程/线程都将处于阻塞状态。通常一个信号量的计数值用于对应有效的资源数, 表示剩下的可被占用的互斥资源数。其值的含义分两种情况:

  • 0:表示没有可用的信号量,进程/线程进入睡眠状态,直至信号量值大于 0。

  • 正值:表示有一个或多个可用的信号量,进程/线程可以使用该资源。进程/线程将信号量值减1, 表示它使用了一个资源单位。

对信号量的操作可以分为两个:

  • P 操作:如果有可用的资源(信号量值大于0),则占用一个资源(给信号量值减去一,进入临界区代码); 如果没有可用的资源(信号量值等于0),则被阻塞,直到系统将资源分配给该进程/线程(进入等待队列, 一直等到资源轮到该进程/线程)。这就像你要把车开进停车场之前,先要向保安申请一张停车卡一样, P操作就是申请资源,如果申请成功,资源数(空闲的停车位)将会减少一个,如果申请失败,要不在门口等,要不就走人。

  • V 操作:如果在该信号量的等待队列中有进程/线程在等待资源,则唤醒一个阻塞的进程/线程。如果没有进程/线程等待它, 则释放一个资源(给信号量值加一),就跟你从停车场出去的时候一样,空闲的停车位就会增加一个。

有名信号量

如果要在Linux中使用信号量同步,需要包含头文件<semaphore.h>。

有名信号量其实是一个文件,它的名字由类似 " sem.[信号量名字] " 这样的字符串组成,注意看文件名前面有" sem. ", 它是一个特殊的信号量文件,在创建成功之后,系统会将其放置在 /dev/shm 路径下,不同的进程间只要约定好一个相同的信号量文件名字,就可以访问到对应的有名信号量,并且借助信号量来进行同步或者互斥操作,需要注意的是,有名信号量是一个文件,在进程退出之后它们并不会自动消失,而需要手动删除并释放资源。

常用 api

sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_close(sem_t *sem);
int sem_unlink(const char *name);

无名信号量

无名信号量的操作与有名信号量差不多,但它不使用文件系统标识,直接存在程序运行的内存中, 不同进程之间不能访问,不能用于不同进程之间相互访问。

常用 api

int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);

参考

https://blog.csdn.net/qq_53508543/article/details/135471988

实践-基于POSIX有名信号量

上一篇博客写了共享内存,给了大家示例的代码。《opencv 打开图片后,cv::mat存入共享内存的代码,以及如何设置共享内存的大小?图片的3840x2160 pixels》,基于此进行生产者消费者尝试。

生产者消费者模型任务说明

生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个著名的进程同步问题的经典案例。它描述的是有一组生产者进程在生产产品,并将这些产品提供给一组消费者进程去消费。为使生产者进程和消费者进程能够并发执行,在这两者之间设置里一个具有 n 个缓冲区的缓冲池,生产者进程将他所生产的的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品并进行消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但亡们之间必须保特同步,即不允许消费者进程到一个空缓冲区中去取产品;也不允许生产者进程向一个已装满产品且产品尚未被取走的缓冲区投放产品。

思路分析
我们分析题目中的同步和互斥关系:

同步关系

当缓冲区有空位时,生产者进程才可以生产
当缓冲区有产品是,消费者进程才可以消费

互斥关系

生产者进程与消费者进程对缓冲区的访问是互斥的

在这里插入图片描述整体思路

总体思路如下:

  • 设置一个生产者进程,负责生产产品
  • 设置一个消费这进程,负责消费产品
  • 生产者与消费者进程间的通讯通过共享内存实现
  • 设置一个互斥信号量,实现对共享内存的互斥访问
  • 设置两个信号量,用于标记资源的数目,实现进程间的两个同步关系

设置一个互斥信号量,实现对共享内存的互斥访问

参考例子

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>int main(int argc, char **argv)
{int pid;sem_t *sem;const char sem_name[] = "my_sem_test";pid = fork();if (pid < 0) {printf("error in the fork!\n");}/* 子进程 */else if (pid == 0) {/*创建/打开一个初始值为1的信号量*/sem = sem_open(sem_name, O_CREAT, 0644, 1);if (sem == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink(sem_name);exit(-1);}/*获取信号量*/sem_wait(sem);for (int i = 0; i < 3; ++i) {printf("child process run: %d\n", i);/*睡眠释放CPU占用*/sleep(1);}/*释放信号量*/sem_post(sem);}/* 父进程 */else {/*创建/打开一个初始值为1的信号量*/sem = sem_open(sem_name, O_CREAT, 0644, 1);if (sem == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink(sem_name);exit(-1);}/*申请信号量*/sem_wait(sem);for (int i = 0; i < 3; ++i) {printf("parent process run: %d\n", i);/*睡眠释放CPU占用*/sleep(1);}/*释放信号量*/sem_post(sem);/*等待子进程结束*/wait(NULL);/*关闭信号量*/sem_close(sem);/*删除信号量*/sem_unlink(sem_name);}return 0;
}

设置两个信号量,用于标记资源的数目,实现进程间的两个同步关系

一个表示空槽位(empty slots)的信号量(empty),用于同步生产者和消费者。当缓冲区为空时,消费者必须等待生产者生成数据。
一个表示满槽位(full slots)的信号量(full),也用于同步生产者和消费者。当缓冲区已满时,生产者必须等待消费者消费数据。

发送

考虑到只有一个内存,存满了就没有了,因此设置信号量队列是1。

    sem_t  * sem_empty = sem_open("/sem_name1", O_CREAT, 0644, 1);if (sem_empty == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink("/sem_name1");exit(-1);}sem_t  * sem_full = sem_open("/sem_name2", O_CREAT, 0644, 0);if (sem_full == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink("/sem_name2");exit(-1);}

处理过程

    // wait for empty slotsem_wait(sem_empty);// get locksem_wait(sem);memcpy(mem, image.data, image.total() * image.elemSize());sem_post(sem);// tell customer to usesem_post(sem_full);

接受

    sem_t  * sem_empty = sem_open("/sem_name1", 0);if (sem_empty == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink("/sem_name1");exit(-1);}sem_t  * sem_full = sem_open("/sem_name2",0);if (sem_full == SEM_FAILED) {printf("unable to create semaphore...\n");sem_unlink("/sem_name2");exit(-1);}

处理过程

    // waitsem_wait(sem_full);// get locksem_wait(sem);cv::Mat image(height, width, type, mem);sem_post(sem);// tell producer to makesem_post(sem_empty);

参考的文心一言的伪代码

// 初始化信号量  
semaphore mutex = 1; // 互斥信号量,初始化为1,表示没有进程在访问共享缓冲区  
semaphore empty = N; // 空槽位信号量,初始化为缓冲区大小N  
semaphore full = 0;  // 满槽位信号量,初始化为0,因为缓冲区开始时是空的  // 生产者进程  
while (true) {  // 生产数据项  item = produce_item();  // 等待缓冲区有空槽位  P(empty); // P操作,即等待(wait)信号量empty变为非零,然后将其减1  // 访问共享缓冲区(临界区)  P(mutex); // 等待mutex变为非零,然后将其减1,进入临界区  // 将数据放入缓冲区  put_item_into_buffer(item);  V(mutex); // V操作,即释放(signal)mutex,将其加1  // 通知消费者缓冲区有数据  V(full); // 释放full信号量,将其加1  
}  // 消费者进程  
while (true) {  // 等待缓冲区有数据  P(full); // 等待full变为非零,然后将其减1  // 访问共享缓冲区(临界区)  P(mutex); // 等待mutex变为非零,然后将其减1,进入临界区  // 从缓冲区取出数据  item = get_item_from_buffer();  V(mutex); // 释放mutex信号量,将其加1  // 处理数据项  consume_item(item);  // 通知生产者缓冲区有空槽位  V(empty); // 释放empty信号量,将其加1  
}

代码分享

https://gitee.com/hiyanyx/share-memory-mmap-opencv/tree/Producer-consumer

下一步

1、如何设置队列?

目前是一个内存,存满了就等待,如何处理呢?

写完了博客,链接:https://blog.csdn.net/djfjkj52/article/details/139806993

2、使用STL 容器在共享内存怎么搞?

STL的容器很方便使用啊,要是能联合,岂不乐哉
写完了博客,链接:https://blog.csdn.net/djfjkj52/article/details/139805285

3、消息队列,例如POSIX 消息队列

<sys/msg.h> 是 Unix 和类 Unix 系统(如 Linux)中用于支持 POSIX 消息队列的头文件。

  • 消息队列是在消息的传输过程中保存消息的容器。它通常是在内核空间中维护的,进程通过特定的标识符(如msgid)对消息队列进行读写操作。

  • 消息队列中的信息通常是一个结构体,包含了消息的种类(类似身份证)和消息的内容。通过这种方式,多个进程间可以通过消息的种类来进行通讯,实现了进程间的定向信息传输。
    -消息队列克服了信号承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 在传输数据方面,消息队列可以使用UDP进行传输。UDP为应用程序提供了一种无需建立连接就可以发送封装的IP数据包的方法。
    消息队列可以基于多种机制进行数据传输,主要可以分为两大类:

    内存中的消息队列:
    这类消息队列直接在系统内存中创建和管理消息,不需要通过网络传输。它们通常用于在同一台机器上的不同进程间进行通信。
    例如,某些消息队列系统(如RabbitMQ、ActiveMQ等)可以在本地机器上运行,通过进程间的共享内存或其他内存管理机制进行消息的存储和传输。
    基于网络的消息队列:
    这类消息队列通过网络协议(如TCP/IP、UDP等)进行消息的传输,允许在不同机器上的进程间进行通信。
    TCP/IP:TCP/IP协议提供了可靠的、面向连接的字节流传输服务。基于TCP/IP的消息队列系统可以确保消息的完整性和顺序性,即使在网络条件不佳的情况下也能保证消息的可靠传输。
    UDP:虽然UDP协议本身不提供可靠的数据传输保证,但某些消息队列系统(如Kafka)可以选择使用UDP进行数据传输。UDP传输速度快,适用于对性能要求极高且对消息丢失或乱序不太敏感的场景。

此外,消息队列的传输方式还取决于具体的消息队列系统和其配置。例如:

  • Kafka:Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量。它通常使用TCP/IP协议进行数据传输,但也可以配置为使用其他协议。
  • RabbitMQ:RabbitMQ是基于AMQP(高级消息队列协议)的开源消息队列系统。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景。它同样使用TCP/IP协议进行数据传输。

归纳来说,消息队列可以基于内存或网络进行数据传输。在网络传输方面,TCP/IP协议由于其可靠性和稳定性而被广泛采用,而UDP则因其高性能和轻量级在某些场景下被选择使用。但具体使用哪种传输方式,还需要根据实际需求、系统架构和网络环境来综合考虑。

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

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

相关文章

铁路防鸟有哪些措施?铁路专用太阳能雷达驱鸟扇

铁路沿线的电杆、接触网支柱以及硬横梁等结构&#xff0c;因其牢固稳定以及无晃动的特点&#xff0c;成为了鸟儿们心仪的筑巢佳地&#xff0c;众多鸟类选择在此“安营扎寨”。而鸟巢中的树枝、草根和铁丝等杂物&#xff0c;极易引起铁路供电设备跳闸&#xff0c;导致短路停电&a…

【云岚到家】-day04-1-数据同步方案-Canal-MQ

【云岚到家】-day04-1-数据同步方案-Canal-MQ 1 服务搜索1.1 服务搜索技术方案1.1.1 需求分析1.1.2 技术方案1.1.2.1 使用Elasticsearch进行全文检索1.1.2.2 索引同步方案 1.1.3 CanalMQ1.1.3.1 MySQL主从数据同步1.1.3.2 Canal工作流程1.1.3.3 具体实现方案 1.2 MQ技术方案1.2…

【QT】

通信服务端实现 widget.h文件 #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTcpServer>//服务器类 #include <QMessageBox>//消息 #include <QTcpServer> #include <QList> #include <QTcpSocket> QT_BEGIN_NAMESPAC…

我在高职教STM32——LCD液晶显示(2)

大家好&#xff0c;我是老耿&#xff0c;高职青椒一枚&#xff0c;一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次&#xff0c;同行应该都懂的&#xff0c;老师在课堂上教学几乎是没什么成就感的。正因如此&#xff0c;才有了借助 CSDN 平台寻求认同感和成就…

Linux源码学习笔记01-Linux内核源码结构

Linux内核特性 是一个类Unix操作系统&#xff0c;但不是简化的Unix&#xff1b;不仅继承了Unix的特征&#xff0c;还有其他特性。 Linux内核的组织形式&#xff1a;整体式的结构&#xff0c;方便每个领域的开发人员参与开发&#xff1b;Linux进程调度方式简单高效&#xff1a…

用智能插件(Fitten Code: Faster and Better AI Assistant)再次修改vue3 <script setup>留言板

<template><div><button class"openForm" click"openForm" v-if"!formVisible">编辑</button><button click"closeForm" v-if"formVisible">取消编辑</button><hr /><formv-i…

【权威发布】2024年环境科学、旅游与产业经济国际会议(ICESTIE 2024)

2024年环境科学、旅游与产业经济国际会议 2024 International Conference on Environmental Science, Tourism and Industrial Economics 会议简介 2024年环境科学、旅游与产业经济国际会议旨在汇聚全球该领域的专家学者&#xff0c;共同探讨环境科学与旅游产业的融合发展&…

SDK编译IO Domain电压选择

开源鸿蒙硬件方案领跑者 触觉智能 本文适用于在Purple Pi OH开发板进行分区镜像烧录。触觉智能的Purple Pi OH鸿蒙开源主板&#xff0c;是华为Laval官方社区主荐的一款鸿蒙开发主板。 该主板主要针对学生党&#xff0c;极客&#xff0c;工程师&#xff0c;极大降低了开源鸿蒙…

【iOS】#include、#import、@class、@import

文章目录 #include#importclassimport总结 #include #include是c\c中的预处理器指令&#xff0c;用于包含头文件的内容 但是使用#include可能会出现重复包含文件的问题&#xff0c;因此需要使用&#xff08;#ifndef/#define/#endif&#xff09;。 #import //导入系统头文件…

使用opencv合并两个图像

本节的目的 linear blending&#xff08;线性混合&#xff09;使用**addWeighted()**来添加两个图像 原理 (其实我也没太懂&#xff0c;留个坑&#xff0c;感觉本科的时候线代没学好。不对&#xff0c;我本科就没学线代。) 源码分析 源码链接 #include "opencv2/imgc…

西米支付:【风控升级】同一商户集中交易,将会限制正常用卡

支付公司风控策略再升级&#xff01;近日&#xff0c;有某支付公司代理透漏&#xff0c;客户反馈机器突然不能刷卡了&#xff0c;换卡也无法交易&#xff0c;交易均提示06-超出商户限额&#xff0c;然而该款机器刷卡限额为单日30万&#xff0c;单月300万&#xff0c;客户并未触…

Win11 设置本地管理员账户的几种方法总结

从设置界面创建 Win11 设置本地管理员账户我们可以在设置界面来进行设置&#xff0c;下面是具体的操作步骤&#xff1a; 首先我们需要打开设置界面&#xff0c;然后点击“账户”选项&#xff0c;进入之后点击“其他用户”。 然后在用户界面中我们找到“其他用户”模块下的添加…

AMD vs NVIDIA:渲染领域的显卡之争

在数字创意与设计的世界里&#xff0c;显卡作为图形处理的核心&#xff0c;其性能与兼容性直接关系到创作者的工作效率与作品质量。AMD与NVIDIA&#xff0c;作为两大显卡巨头&#xff0c;各自在渲染领域拥有独特的技术与优势。那么&#xff0c;针对渲染而言&#xff0c;哪种显卡…

四川古力未来科技有限公司抖音小店解锁电商新机遇

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度蓬勃发展。四川古力未来科技有限公司紧跟时代步伐&#xff0c;积极拥抱变革&#xff0c;在抖音平台上开设小店&#xff0c;为品牌发展注入了新的活力。那么&#xff0c;四川古力未来科技有限公司抖音小店究竟…

C#和python端通信之使用共享内存

一、前言 本篇主要实验通过使用共享内存实现C#端代码和python端代码之间的通信&#xff0c;主要目的是相较于直接传输较大的数据&#xff08;例如图像数据&#xff09;&#xff0c;该方式更节省时间。 二、代码 C#端&#xff1a; 创建了一个大小为1的共享内存&#xff0c;名为…

Js-w3school(2024(1)

10.访问 HTML 元素 使用“不整洁的” HTML 样式的后果&#xff0c;也许是导致 JavaScript 错误。请在 HTML 中使用相同的命名约定&#xff08;就像 JavaScript 那样&#xff09; 11. 请使用正确的文档类型 请始终在文档的首行声明文档类型&#xff1a; 如果您一贯坚持小写标…

神经网络学习-池化层

池化层方法 池化一般是用来对卷积层进行降维 空洞卷积&#xff0c;通过在卷积核的元素之间插入“空洞”&#xff08;即零&#xff09;&#xff0c;可以在不增加参数量和计算量的情况下扩大卷积核的感受野。这对于捕捉图像中的多尺度信息特别有用。 池化的默认步长是池化核的大…

vscode插件开发之 - 消息通信

在开发vscode插件过程中&#xff0c;有一个典型场景是webview与extension.ts进行通信&#xff0c;例如&#xff0c;webview上的某些信息发送改变时&#xff0c;需要发送消息传递给extension.ts. 如果使用react框架构建vscode插件的webview&#xff0c;如何实现webview与extensi…

工厂ESOP系统促进工厂快速响应和工艺改进

在当今追求可持续发展和创新的时代&#xff0c;新能源产业正以惊人的速度崛起。新能源工厂作为这一领域的核心生产环节&#xff0c;面临着不断提高效率、优化工艺和快速应用新技术的巨大挑战。为了应对这些挑战&#xff0c;越来越多的新能源工厂开始引入 ESOP 系统&#xff08;…

为什么企业需要数据挖掘平台?哪个比较好呢?

什么是数据挖掘&#xff1f; 数据挖掘就是从大量的数据中去发现有用的信息&#xff0c;然后根据这些信息来辅助决策。听起来是不是跟传统的数据分析很像呢&#xff1f;实际上&#xff0c;数据挖掘就是智能化的数据分析&#xff0c;它们的目标都是一样的。但是&#xff0c…