共享内存同步问题

本文介绍了一个使用共享内存和信号量进行进程间通信的例子。通过两个进程分别扮演生产者和消费者角色,展示了如何利用信号量控制共享内存的访问,确保数据的一致性和同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号量,信号:http://blog.youkuaiyun.com/killmice/article/details/41516533

信号量代码:http://www.360doc.com/content/14/0803/10/16285334_399081525.shtml

 

一个更全面的信号量例子:http://blog.youkuaiyun.com/qinxiongxu/article/details/7830537/

无锁队列: http://www.oschina.net/translate/a-fast-lock-free-queue-for-cpp?cmp



共享内存查看

 ipcs

删除共享内存

ipcrm <shmid>

一下是一个完整的例子,用信号量做进程间共享内存同步

sharemem.h

#ifndef _SHAREMEM_H_
#define _SHAREMEM_H_
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <iostream>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include<errno.h>
#define SHMKEY (key_t) 0x100 
#define SEMKEY (key_t) 0x200 

#define IFLAGS (IPC_CREAT|IPC_EXCL) 
#define ERR ((void*) -1)
#define MSG_HEADER_SIZE 8
#define SHM_SIZE 1048576 //1024 * 1024
#define SEM_OP_MAX 2 //
#define SEM_OP_MIN 0

union semun
{
	int val; //信号量初始值                   
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

//sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下
//struct sembuf
//{
//	unsigned short sem_num; /* semaphore number */
//	short sem_op; /* semaphore operation */
//	short sem_flg; /* operation flags */
//};

//sem_num:操作信号在信号集中的编号,第一个信号的编号是0。
//sem_op:
//如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;
//如果为负数,而其绝对值又大于信号的现值,操作将会阻塞,
//直到信号值大于或等于sem_op的绝对值。
//通常用于获取资源的使用权;
//如果sem_op的值为0,如果没有设置IPC_NOWAIT,
//则调用该操作的进程或者线程将暂时睡眠,直到信号量的值为0;
//否则,进程或者线程不会睡眠,函数返回错误EAGAIN。
//sem_flg:信号操作标志,可能的选择有两种
//IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
//SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。
//这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
struct sembuf 
	p0 = { 0, -1, 0},    //本组第1个信号量,阻塞加1,
	p1 = { 1, -1, 0},    //本组第2个信号量,阻塞加1,
	v0 = { 0, 1, 0},     //本组第1个信号量,放开加1,
	v1 = { 1, 1, 0};     //本组第2个信号量,放开加1,

#pragma  pack(push)  //让原字节对齐方式入编译器栈
#pragma  pack(1)    //指定新的对齐方式, 1字节对齐.  (数字即几字节对齐)
struct DataUnit {
	int32_t type_;
	int32_t size_;
	char* data_ptr_;

	int32_t Append(const char* dist_str)
	{
		std::string data;
		data.append(data_ptr_, size_);
		std::cout << type_ << "\t"
			<< size_ << "\t"
			<< data << std::endl;

		memcpy((void*)dist_str,       (void*)&type_,    4);
		memcpy((void*)(dist_str + 4), (void*)&size_,    4);
		memcpy((void*)(dist_str + MSG_HEADER_SIZE), (void*)data_ptr_, size_);
		//memcpy((void*)dist_str, (void*)this, sizeof(DataUnit));

		return 0;
	}

	int32_t GetSize()
	{
		return 8 + size_;
	}

	static void Output(const char* str)
	{
		DataUnit unit;
		memcpy((void*)(&unit.type_), (void*)str , 4);
		memcpy((void*)(&unit.size_), (void*)(str + 4), 4);
		std::string data;
		data.append(str + MSG_HEADER_SIZE, unit.size_);
		std::cout << unit.type_ << "\t"
			<< unit.size_ << "\t" 
			<< data << std::endl;
	}

	static int32_t GetCurrentSize(const char* str)
	{
		int32_t size = 0;
		memcpy((void*)(&size), (void*)(str + 4), 4);
		return MSG_HEADER_SIZE + size;
	}
};
#pragma pack(pop)//出栈原来对齐方式

inline void pr_error(const char *mess)
{
	perror(mess);
	//exit(1);
}

inline void GetShareMem(int& shmid, void*& share_mem_ptr)
{
	/*取得共享内存的key,如果还没建立的话,就建立(IPC_CREAT)*/
	if ((shmid = shmget(SHMKEY, SHM_SIZE, 0600 | IFLAGS)) < 0)
	{
		if (errno == EEXIST)
		{
			shmid = shmget(SHMKEY, SHM_SIZE, 0600 | IPC_CREAT);
			if (shmid < 0)
			{
				pr_error("shmget");
			}
		}
		else
		{
			pr_error("shmget");
		}
	}
	/*取得共享内存的指针,使用共享内存时可以和malloc分配的内存一样*/
	if ((share_mem_ptr = (shmat(shmid, 0, 0))) == ERR)
	{
		pr_error("shmat");
	}
}

inline void GetSem(int& semid)
{ /*建立信号量(且称为A和B)*/
	if ((semid = semget(SEMKEY, 2, 0600 | IFLAGS)) < 0)
	{
		if (errno == EEXIST)
		{
			semid = semget(SEMKEY, 2, 0600 | IPC_CREAT);
			if (semid < 0)
			{
				pr_error("semget");
			}
			else
			{
				/*加上这一句,如果已存在,不再初始化信号量*/
				return;
			}
		}
		else
		{
			pr_error("semget");
		}
	}
	/*设置信号量A初值为0*/
	//semctl()的第一个参数是信号量集IPC标识符。
	//第二个参数是操作信号在信号集中的编号,第一个信号的编号是0。
	union semun init_min, init_max;
	init_min.val = SEM_OP_MIN;
	init_max.val = SEM_OP_MAX;
	if (semctl(semid, 0, SETVAL, init_min) < 0)
	{
		pr_error("semctl");
	}
	/*设置信号量B初值为1*/
	if (semctl(semid, 1, SETVAL, init_max) < 0)
	{
		pr_error("semctl");
	}

	//std::cout << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;
}

inline void RemoveIPC(int shmid, int semid)
{
	/*删除共享内存*/
	if (shmctl(shmid, IPC_RMID, 0) < 0)
		pr_error("shmctl");
	/*删除信号量*/
	if (semctl(semid, IPC_RMID, 0) < 0)
		pr_error("semctl");
}

#endif




systemv_produce.c


#include "sharemem.h"

int shmid, semid;
void* share_mem_ptr = NULL;   //取得共享内存的指针

void Writer()
{
	srand((unsigned)time(NULL));
	char buf_data[256];
	memset(buf_data, 0, 256);
	memcpy(buf_data, "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", 90);
	std::cout << buf_data << std::endl;

	short dcc = SEM_OP_MAX;
	

	DataUnit buf;
	buf.data_ptr_ = &buf_data[0];
	int64_t current_index = 0;
	int i, j;
	for (i = 0; i < 10; i++)
	{
		buf.type_ = rand() % 10 + 1;
		buf.size_ = rand() % 20 + 1;
		
		//semctl(semid, p0.sem_num, GETVAL)得到semid当前当前值
		//int semop(int semid, struct sembuf semoparray[], size_t nops);  
		//semoparray是一个指针,它指向一个信号量操作数组。
		//nops规定该数组中操作的数量。

		std::cout << "start " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;

		semop(semid, &p1, 1);   //写信号量阻塞加1,也就是队列可写长度减1

		std::cout << "step1 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;

		buf.Append((char*)share_mem_ptr + current_index);   //写共享内存
		semop(semid, &v0, 1);   //读信号量释放加1,也就是可读队列长度加1

		std::cout << "step2 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;
		std::cout << std::endl;

		current_index += buf.GetSize();
	}
	std::cout << "step2 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;
	return;
}

main()
{
	GetSem(semid);
	GetShareMem(shmid, share_mem_ptr);
	Writer();
	RemoveIPC(shmid, semid);
	exit(0);
}

//1.信号量结构体
//内核为每个信号量集维护一个信号量结构体,可在<sys / sem.h>找到该定义:
//struct semid_ds {
//    struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */
//    struct sem *sem_base; /* 某个信号量sem结构数组的指针,当前信号量集
//                          中的每个信号量对应其中一个数组元素 */
//    ushort sem_nsems; /* sem_base 数组的个数 */
//    time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */
//    time_t sem_ctime; /* 成功创建时间 */
//};
//struct sem {
//    ushort semval; /* 信号量的当前值 */
//    short sempid; /* 最后一次返回该信号量的进程ID 号 */
//    ushort semncnt; /* 等待semval大于当前值的进程个数 */
//    ushort semzcnt; /* 等待semval变成0的进程个数 */
//};
//(d)设置信号量的值(PV操作)
//int semop(int semid, struct sembuf *opsptr, size_t nops);
//(1) semid: 是semget返回的semid
//(2)opsptr: 指向信号量操作结构数组
//(3) nops : opsptr所指向的数组中的sembuf结构体的个数
//struct sembuf {
//    short sem_num; // 要操作的信号量在信号量集里的编号,
//    short sem_op; // 信号量操作
//    short sem_flg; // 操作表示符
//};
//(4) 若sem_op 是正数,其值就加到semval上,即释放信号量控制的资源
//若sem_op 是0,那么调用者希望等到semval变为0,如果semval是0就返回;
//若sem_op 是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值



systemv_consumer.c


#include "sharemem.h"

int shmid, semid;
void* share_mem_ptr = NULL;   //取得共享内存的指针

void Reader()
{
	int64_t current_index = 0;
	int i, j;
	for (i = 0; i < 10; i++)
	{
		//如果没数据的话,blocking,有的话,读
		std::cout << "start " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;

		semop(semid, &p0, 1);    //读信号量阻塞加1,也就是队列可读长度减1
		std::cout << "step1 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;

		DataUnit::Output((char*)share_mem_ptr + current_index);
		current_index += DataUnit::GetCurrentSize((char*)share_mem_ptr + current_index);

		semop(semid, &v1, 1);    //写信号量释放加1,也就是队列可写长度加1
		std::cout << "step2 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;
		std::cout << std::endl;
	}
	std::cout << "step2 " << semctl(semid, 0, GETVAL, 0) << "\t" << semctl(semid, 1, GETVAL, 0) << std::endl;
	return;
}

main()
{
	GetSem(semid);
	GetShareMem(shmid, share_mem_ptr);
	Reader();
	RemoveIPC(shmid, semid);
	exit(0);

}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值