linux c多进程通信之共享内存和信号量

article/2024/5/23 2:43:41

编译环境:Ubuntu16.04 64位
交叉编译工具:arm-hisiv500-linux-gcc

文章目录

  • 1. 项目背景
  • 2. 涉及的函数
  • 3. 头文件JShm.h
  • 4. 类的实现
  • 5. sample代码

1. 项目背景

最近项目中需要用到共享内存的交互,取走旧数据,取数据不及时写入覆盖旧数据,队列长度可配置,自己造个轮子吧,对照下一篇linux c多线程简单队列实现。

2. 涉及的函数

详细描述可以百度或ubuntu下查询man手册。
共享内存相关:
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

信号量相关:
int semget(key_t key,int nsems,int semflg);
int semctl(int semid,int semnum,int cmd, /union semun arg/);
semctl是可变参数长度的函数
int semop(int semid, struct sembuf *sops, size_t nsops);

3. 头文件JShm.h

#ifndef __J_SHM__
#define __J_SHM__#ifdef __cplusplus
extern "C" {
#endiftypedef enum _JShm_ERR{JSHM_NO_DATA = -4,				// 无数据供读取JSHM_READ_LENGTH_ERR = -3,		// 读取缓冲区长度不够JSHM_WRITE_LENGTH_ERR = -2,	// 写入共享内存长度超出JSHM_INIT_FAIL = -1,			// 初始化异常JSHM_OK = 0,JSHM_WRITE_OVERWRITE = 1,		// 写成功,但是覆盖
}JShm_ERR;class JShm {
public:JShm(int key, unsigned int size, unsigned int blockNum = 0);~JShm();int Write(unsigned char *buf, unsigned int len);int Read(unsigned char *buf, unsigned int len);private:int Init();int UnInit();int SemP();int SemV();static unsigned int IsPower2(unsigned int size);	static unsigned int To2N(unsigned int size);
private:int m_shmKey;unsigned int m_size;		// 共享内存大小unsigned int m_blockSize;	// 块大小unsigned int m_blockNum;	// 块数量unsigned char *m_buffer; 	// 共享内存地址unsigned int m_offset;		// 数据偏移量int m_shmID;int m_semID;
};#ifdef __cplusplus
}
#endif
#endif // __J_SHM__

4. 类的实现

#include <errno.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>#include "JShm.h"union SemUnion {int val;struct semid_ds *buf;
};struct ShmHead {unsigned int readPos;	// 读取位置,从0开始,blockNum不为0时有效unsigned int dataNum;	// 数据数量unsigned int len[0];	// 数据长度,根据blockNum分配
};unsigned int JShm::IsPower2(unsigned int size)
{if (size == 0)return 0;return ((size & (size - 1)) == 0);
}unsigned int JShm::To2N(unsigned int size)
{unsigned int i = 1;unsigned int tmp = size;while (size >>= 1){i <<= 1;}return (i < tmp) ? i << 1 : i;
}// 构造时,请务必保证双方的参数相同
// blockNum默认为0,不传时表示只有1块无缓存
JShm::JShm(int key, unsigned int blockSize, unsigned int blockNum)
{m_shmKey = key;unsigned int nSize = IsPower2(blockSize) ? blockSize : To2N(blockSize);m_blockSize = nSize;m_blockNum = (blockNum == 0) ? 1 : blockNum;m_offset = sizeof(ShmHead) + sizeof(unsigned int) * m_blockNum;m_size = m_blockSize * m_blockNum + m_offset;m_buffer = NULL;m_shmID = -1;m_semID = -1;Init();
}JShm::~JShm()
{UnInit();
}int JShm::Init()
{m_shmID = shmget(m_shmKey, m_size, IPC_CREAT | IPC_EXCL | 0666);if (m_shmID < 0){if (errno != EEXIST){return JSHM_INIT_FAIL;}m_shmID = shmget(m_shmKey, m_size, IPC_CREAT | 0666);if (m_shmID < 0){m_shmID = shmget(m_shmKey, 0, 0666);if (m_shmID < 0){return JSHM_INIT_FAIL;}if (shmctl(m_shmID, IPC_RMID, NULL)!= 0){return JSHM_INIT_FAIL;}m_shmID = shmget(m_shmKey, m_size, IPC_CREAT | IPC_EXCL | 0666);if (m_shmID < 0){return JSHM_INIT_FAIL;}}}m_buffer = (unsigned char *)shmat(m_shmID, NULL, 0);if (m_buffer == (unsigned char *)-1){return JSHM_INIT_FAIL;}memset(m_buffer, 0, m_size);m_semID = semget(m_shmKey, 1, 0666 | IPC_CREAT);if (m_semID < 0){return JSHM_INIT_FAIL;}union SemUnion semUnion;semUnion.val = 1;semctl(m_semID, 0, SETVAL, semUnion);return JSHM_OK;
}int JShm::UnInit()
{shmdt(m_buffer);m_buffer = NULL;shmctl(m_shmID, IPC_RMID, NULL);m_shmID = -1;semctl(m_semID, 0, IPC_RMID);m_semID = -1;return JSHM_OK;
}int JShm::SemP()
{// 申请信号,信号量-1sembuf semP;semP.sem_num = 0;semP.sem_op = -1;semP.sem_flg = SEM_UNDO;semop(m_semID, &semP, 1);return JSHM_OK;
}int JShm::SemV()
{// 释放信号,信号量+1sembuf semV;semV.sem_num = 0;semV.sem_op = 1;semV.sem_flg = SEM_UNDO;semop(m_semID, &semV, 1);return JSHM_OK;
}// !!!note:写入共享内存,写入长度需要小于等于块长度
// buf:要写入的数据
// len:要写入的数据的长度
// 返回值:>=0表示成功,0成功,1覆盖旧数据,-1表示失败
int JShm::Write(unsigned char *buf, unsigned int len)
{if (m_buffer == NULL)return JSHM_INIT_FAIL;if (len > m_blockSize)return JSHM_WRITE_LENGTH_ERR;	int ret = JSHM_OK;SemP();ShmHead *head = (ShmHead *)m_buffer;	unsigned int writePos = head->readPos + head->dataNum;if (writePos >= m_blockNum)writePos = writePos - m_blockNum;memcpy(m_buffer + m_offset + m_blockSize * writePos, buf, len);head->len[writePos] = len;if (head->dataNum == m_blockNum) // 满的{head->readPos = (writePos + 1 >= m_blockNum) ? 0 : writePos + 1;ret = JSHM_WRITE_OVERWRITE;}else{head->dataNum = head->dataNum + 1;}SemV();return ret;
}// !!!note:读取共享内存,读取长度需要大于等于数据长度
// buf:读数据的缓冲区
// len:缓冲区的长度
// 返回值:实际读取到的长度,<=0表示失败
int JShm::Read(unsigned char *buf, unsigned int len)
{if (m_buffer == NULL)return JSHM_INIT_FAIL;int ret = JSHM_OK;SemP();ShmHead *head = (ShmHead *)m_buffer;if (head->dataNum == 0){SemV();return JSHM_NO_DATA;}unsigned int readPos = head->readPos;if (len < head->len[readPos]){SemV();	return JSHM_READ_LENGTH_ERR;}memcpy(buf, m_buffer + m_offset + m_blockSize * readPos, head->len[readPos]);ret = head->len[readPos];head->readPos = (readPos + 1 >= m_blockNum) ? 0 : readPos + 1;head->dataNum = head->dataNum - 1;SemV();return ret;
}

5. sample代码

#include <stdio.h>#define SHM_KEY   101
#define SHM_BLOCKSIZE  1024
#define SHM_BLOCKNUM 2
int main(int argc, char* argv[])
{if (argc < 2){return -1;}if (argv[1][0] == 'w'){JShm *ShmWrite = new JShm(SHM_KEY, SHM_BLOCKSIZE, SHM_BLOCKNUM);while (true){char writeBuf[128] = {0};fgets(writeBuf, sizeof(writeBuf), stdin);if (writeBuf[0] == 'q'){delete ShmWrite;return 0;}printf("write data:%s\n", writeBuf);ShmWrite->Write((unsigned char *)writeBuf, strlen(writeBuf));}} else if (argv[1][0] == 'r') {JShm *ShmRead = new JShm(SHM_KEY ,SHM_BLOCKSIZE, SHM_BLOCKNUM);while (true) {char writeBuf[128] = {0};fgets(writeBuf, sizeof(writeBuf), stdin);if (writeBuf[0] == 'q'){delete ShmRead;return 0;}unsigned char readBuf[128] = { 0 };int ret = ShmRead->Read(readBuf, 127);printf("read %d data:%s\n", ret, readBuf);}}return 0;
}

编译完成之后,ubuntu下分别执行./a.out w和./a.out r测试。

以上。
转载请注明出处,如有错漏之处,敬请指正。


http://www.ngui.cc/article/show-2022962.html

相关文章

代码随想录算法训练营第三十八天| 509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯

题目与题解 参考资料&#xff1a;动态规划基础 动态规划五步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 题目链接&#xff1a;​​​​​​​509. 斐波那契数 代码随想录题解&am…

基于ethers.js连接小狐狸快速开发

1:安装 以太坊中各种类和函数都可以从ethersproject下的子库中手动进行导入&#xff0c;但对于大多数项目来说&#xff0c;用完整的总库是最简单的入门方式。 npm install --save ethers2:导入 const { ethers } require("ethers"); import { ethers } from "…

已解决java.util.TooManyListenersException: 监听器过多异常的正确解决方法,亲测有效!!!

已解决java.util.TooManyListenersException: 监听器过多异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 阅读文档 代码审查 资源管理 总结 博主v&#xff1a;XiaoMing_Java 问题分析 java.…

LLamaSharp加载llama.cpp转化好的模型

新建.net8控制台项目 安装依赖包 LLamaSharp和LLamaSharp.Backend.Cpu 准备好转化好的模型 没有的话参考这篇文章https://blog.csdn.net/qq_36437991/article/details/137248622 编写代码 using LLama; using LLama.Common; using LLama.Native;namespace llamasharpstu…

学习基于pytorch的VGG图像分类 day4

注&#xff1a;本系列博客在于汇总CSDN的精华帖&#xff0c;类似自用笔记&#xff0c;不做学习交流&#xff0c;方便以后的复习回顾&#xff0c;博文中的引用都注明出处&#xff0c;并点赞收藏原博主. 目录 VGG模型检测 一&#xff1a;导入必要的库和模块 二&#xff1a;主函数…

772. 只出现一次的字符

给你一个只包含小写字母的字符串。 请你判断是否存在只在字符串中出现过一次的字符。 如果存在&#xff0c;则输出满足条件的字符中位置最靠前的那个。 如果没有&#xff0c;输出 no。 输入格式 共一行&#xff0c;包含一个由小写字母构成的字符串。 数据保证字符串的长度…

C#Winform使用扩展方法自定义富文本框(RichTextBox)字体颜色

实现效果 调用方法 rtxtLog.AppendTextColorful(richTextBox1,DateTime.Now.ToString(), Color.Red); 完整代码如下 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Sys…

Java内存泄漏内存溢出

1.定义 OOM内存溢出是指应用程序尝试使用更多内存资源&#xff0c;而系统无足够的内存&#xff0c;导致程序崩溃。 内存泄漏是指应用程序中分配的内存未能被正确释放&#xff0c;导致系统中的可用内存逐渐减少。 2.内存泄漏的原因 可能包括对象引用未被释放、缓存未被清理等。 …

云计算:OVS 集群 使用 Geneve 流表

目录 一、实验 1.环境 2.OVS 集群 使用 Geneve 流表 二、问题 1.VXLAN与Geneve区别 一、实验 1.环境 (1) 主机 表1 宿主机 主机架构软件IP网卡备注ovs_controller控制端 karaf 0.7.3 192.168.204.63 1个NAT网卡 &#xff08;204网段&#xff09; 已部署ovs_server01服务…

idea keymap用eclipse的相关快捷键

idea快捷键用eclipse的方式 CtrlShiftR 搜索文件 shiftshift 全部文件/类搜索 CtrlH 全局搜索 CtrlO 快速打开Outline大纲视图 ctrle 查看recent窗口文件 ctrlt 快速进入接口的实现类 ctrlshiftf 格式化代码 altshiftr 变量或函数的重命名 ctrlshifto 移除无用的头文…