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

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

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" {
#endif

typedef 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, IPC_CREAT | IPC_EXCL | 0666);
	if (m_semID < 0)
	{
		if (errno != EEXIST)
		{
			return JSHM_INIT_FAIL;
		}
		m_semID = semget(m_shmKey, 1, 0666);
		if (m_semID < 0)
		{
			return JSHM_INIT_FAIL;
		}
	}
	else
	{	
	    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()
{
    // 申请信号,信号量-1
	sembuf 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()
{
	// 释放信号,信号量+1
    sembuf 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测试。

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

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值