Linux利用共享内存实现多进程通信非堵塞FIFO

       最近传输音视频流数据用到了共享内存进行多线程通信,遇到了一些bug。就想着自己实现一个比较可靠的多线程共享数据通信。搞了一天,其中也遇到了一点坑,坑了我很久,记录一下,避免日后再踩坑。

       首先,共享内存属于多进程的共享资源,必须保证读写是安全的,要确保一个进程在写的时候不能被读,在一个进程读的时候,其他进程不能写。所以可以选择信号量做同步,也可以选择互斥量做同步。这里我选择了互斥量,因为互斥量时间开销比信号量小,而且后续可以结合条件变量做成堵塞的FIFO (First In First Out)。

关于互斥量与信号量的性能比较可以参考这篇文章:

https://www.cnblogs.com/webber1992/p/6550667.html

实际使用中,往往是一个进程写,一个进程读。

思路:用一个结构体来存储共享内存的相关信息

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>

struct SHARE_BUF_MSG
{
	unsigned int    magic_key;	// buf key
	unsigned int    buffer_size;	// total buffer_size
	unsigned int    valid_size;      //valid data count
	unsigned int    writeptr;        //  write pointer
	unsigned int    readptr;        //  write pointer
	pthread_mutex_t rw_mutex;      //read write lock
	//unsigned char * buffer;				// buffer to save;
};

struct SHARE_BUFFER
{
	struct SHARE_BUF_MSG *msg;
	unsigned char * buffer;				// buffer to save;

};

typedef struct
{
	unsigned int    magic;   //SHBUF_MAGIC_NUM用于数据包检验
	unsigned int    packlen;	// pakage data size
}SHBUF_mnghead;

这里我犯了个错误,就是把数据buffer也放在了结构体SHARE_BUF_MSG 里面,搞了半天,总是出现segment fault,两个读写进程不能同时存在。。

        初始化,申请两块共享内存,一块是SHARE_BUF_MSG,一块是存数据用的buffer。一开始我是打算申请一大块共享内存,前面少量字节作为SHARE_BUF_MSG用,剩下的作为buffer用(后来发现也是可以的)。就是因为刚开始我把buffer这个指针成员也放进了SHARE_BUF_MSG,存放着共享内存的地址,这个指针占的空间本身也在共享内存里,导致第一个进程运行之后,再运行第二个进程,初始化就出现segment fault了。因为第二个进程申请的buffer地址是逻辑地址,和第一个进程的逻辑地址不一样,buffer指针的值被第二个进程修改了,所以第一个进程访问的buffer就不对了,所以就segment fault了。

void *SHARE_BUFFER_Init(int size,int infoid,int bufid)
{
	struct SHARE_BUFFER *buffer_share=NULL;
	struct SHARE_BUF_MSG *buffer_msg = NULL;
	if(size <=0)
		return NULL;
	
	buffer_share = calloc(sizeof(struct SHARE_BUFFER),1);
	if(buffer_share == NULL)
		return NULL;

	int mid = shmget(infoid,  sizeof(struct SHARE_BUF_MSG) , IPC_CREAT | 0660);
	if(mid < 0){
		printf("memory already create\n");
		mid = shmget(infoid, 0, 0);
		if(mid < 0)
			return NULL;
	}
	buffer_share->msg = (struct SHARE_BUF_MSG *)shmat(mid,NULL,0);
	if(buffer_share->msg == NULL)
	{
		perror("shmat addr error") ;
		printf("buf = NULL\n");
		return NULL;
	}
	else
	{
		mid = shmget(bufid,  size , IPC_CREAT | 0660 );
		if(mid < 0)
		{
			printf("memory already create\n");
			mid = shmget(bufid, 0, 0);
			if(mid < 0)
			{
				perror("shmget  error") ;
				return NULL;
			}
		}
	
		buffer_share->buffer = shmat(mid,NULL,0);
		if(buffer_share->buffer == NULL)
		{
			perror("shmat addr error") ;
			return NULL;
			
		}
		if(buffer_share->msg->magic_key != bufid)
		{
			memset(buffer_share->buffer,0,size);	
			buffer_share->msg->magic_key = bufid;
			buffer_share->msg->buffer_size = size;
			pthread_mutexattr_t mutexattr;
			pthread_mutexattr_init(&mutexattr);         // 初始化 mutex 属性
			pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);               // 修改属性为进程间共享
			pthread_mutex_init(&buffer_share->msg->rw_mutex,&mutexattr);      // 初始化一把 mutex 锁
			printf("init!\n");
		}
		//sem_init(SEM_KEY1);
	}
	
	return (void *)buffer_share;
}

共享内存读函数

//内部buffer操作函数,主要是注意处理buffer边界问题,读指针偏移
void ShareBufferRead(unsigned char *destbuf, int len, struct SHARE_BUF_MSG *buffer_msg, unsigned char *shmbuffer)
{
	if(destbuf == NULL || buffer_msg == NULL)
		return -1;
	
	if((buffer_msg->buffer_size - buffer_msg->readptr) >= len)
	{
		memcpy(destbuf, shmbuffer+ buffer_msg->readptr,len);
	}
	else
	{
		memcpy(destbuf, shmbuffer+buffer_msg->readptr, buffer_msg->buffer_size - buffer_msg->readptr);
		memcpy(destbuf+buffer_msg->buffer_size - buffer_msg->readptr, shmbuffer, len-(buffer_msg->buffer_size - buffer_msg->readptr));		
	}	

}



//读函数
int SHARE_BUFFER_read_API(struct SHARE_BUF_MSG *buffer_msg, unsigned char *shmbuffer, unsigned char *data,int datasize)  //,unsigned int registerID)
{
	unsigned int headsize,len;
	SHBUF_mnghead head;
	//struct readcortrol *readusr;

	headsize = sizeof(SHBUF_mnghead);
	if((buffer_msg == NULL)||(data == NULL)||(datasize <=0))
	{
		printf("buffer_msg == 0x%08x,  data == 0x%08x,    datasize = %d\n",(unsigned int)buffer_msg,(unsigned int)data,datasize);
		return -1;

	}
	//取锁	
	//sem_p();
	pthread_mutex_lock(&buffer_msg->rw_mutex);
	//取共享内存头
	ShareBufferRead((unsigned char *)&head, headsize, buffer_msg, shmbuffer);
	len = head.packlen;
	//读取的数据如果小于一包的数据,不读
	if(datasize < len || buffer_msg->valid_size==0 || head.magic != SHBUF_MAGIC_NUM)
	{
		printf("magic %x, datasize =%d len =%d \n",head.magic,datasize,len);
		//sem_v();	
		pthread_mutex_unlock(&buffer_msg->rw_mutex);
		return -1;
	}
	//更新读指针
	buffer_msg->readptr = (buffer_msg->readptr + headsize)% buffer_msg->buffer_size;
	
	ShareBufferRead(data,  len, buffer_msg, shmbuffer);
	//更新读指针
	buffer_msg->readptr = (buffer_msg->readptr + len)% buffer_msg->buffer_size;
	//更新可读数量
	buffer_msg->valid_size = buffer_msg->valid_size - headsize - len ;
	printf("valid_size=%d\n",buffer_msg->valid_size);
	//释放锁
	//sem_v();
	pthread_mutex_unlock(&buffer_msg->rw_mutex);
	return len;
}

共享内存写函数

int SHARE_BUFFER_write_API(struct SHARE_BUF_MSG *buffer_msg,unsigned char *shmbuffer,unsigned char *data,unsigned int datasize)
{
	unsigned int mngheadsize;
	SHBUF_mnghead mnghead;

	mngheadsize = sizeof(SHBUF_mnghead);
	if((buffer_msg == NULL)||((data == NULL)&&(datasize>0)))
		return -1;
	
	//sem_p();
	pthread_mutex_lock(&buffer_msg->rw_mutex);

	printf("getwrite lock!\n");
	if((buffer_msg->valid_size +datasize + mngheadsize) >= buffer_msg->buffer_size)
	{
		//sem_v();
		pthread_mutex_unlock(&buffer_msg->rw_mutex);
		printf("release write lock!\n");
		return -1;
	}
	
	//用于校验共享的数据头
	mnghead.magic = SHBUF_MAGIC_NUM;   
	//一包数据的长度
	mnghead.packlen = datasize;
	{
		//写入共享内存头
		if(mngheadsize > (buffer_msg->buffer_size - buffer_msg->writeptr))
		{
			memcpy(shmbuffer+buffer_msg->writeptr,&mnghead,buffer_msg->buffer_size - buffer_msg->writeptr);
			memcpy(shmbuffer,(char *)&mnghead+(buffer_msg->buffer_size - buffer_msg->writeptr),mngheadsize - (buffer_msg->buffer_size - buffer_msg->writeptr));
		}
		else
		{
			memcpy(shmbuffer+buffer_msg->writeptr,&mnghead,mngheadsize);
		}
		//更新写指针
		buffer_msg->writeptr = (buffer_msg->writeptr + mngheadsize)% buffer_msg->buffer_size;
	}
	//拷贝数据
	if(datasize!=0)
	{
		if(datasize>(buffer_msg->buffer_size - buffer_msg->writeptr))
		{
			memcpy(shmbuffer+buffer_msg->writeptr,data,buffer_msg->buffer_size - buffer_msg->writeptr);
			memcpy(shmbuffer,data+(buffer_msg->buffer_size - buffer_msg->writeptr),datasize - (buffer_msg->buffer_size - buffer_msg->writeptr));
		}
		else
		{
			memcpy(shmbuffer+buffer_msg->writeptr,data,datasize);
		}
		buffer_msg->writeptr= (buffer_msg->writeptr + datasize)% buffer_msg->buffer_size;
	}
	buffer_msg->valid_size = buffer_msg->valid_size  + datasize + mngheadsize;
	//释放锁
	//sem_v();
	pthread_mutex_unlock(&buffer_msg->rw_mutex);
	printf("write done!\n");
	return 0;
}

测试写 write.c

int main()
{
	int ret=0;
	struct SHARE_BUFFER *shareBuffer = SHARE_BUFFER_Init(BUFFER_SIZE, SHARE_BUF_INFO_KEY, SHARE_BUF_KEY);
	if(shareBuffer==NULL)
	{
		printf("malloc error!\n");
		return -1;
		
	}
	printf("mallac  success!\n");
	char data[50]="Never say nothing to do, something worth to try!\n"; 
	
	while(1)
	{
		sleep(1);
		ret = SHARE_BUFFER_write_API(shareBuffer->msg,shareBuffer->buffer,data,50);
		printf("write ret=%d\n",ret);
	}
	free(shareBuffer);
	return 0;	
}

测试读 read.c


int main()
{
	int ret=0;
	struct SHARE_BUFFER *shareBuffer = SHARE_BUFFER_Init(BUFFER_SIZE, SHARE_BUF_INFO_KEY, SHARE_BUF_KEY);
	if(shareBuffer==NULL)
	{
		printf("malloc error!\n");
		return -1;
		
	}
	printf("mallac  success!\n");
	char data[50]={0};//"Never say nothing to do,something worth to do!\n"; 
	while(1)
	{
		sleep(2);
		memset(data,0,50);
		ret = SHARE_BUFFER_read_API(shareBuffer->msg, shareBuffer->buffer, data,50) ;
		printf("read ret=%d\n",ret);
		printf("read string:%s\n",data);
	}
	return 0;
	
}

先运行读进程,再运行写进程,运行结果:

init!
mallac  success!
magic 0, datasize =50 len =0 
read ret=-1
read string:
magic 0, datasize =50 len =0 
read ret=-1
read string:
magic 0, datasize =50 len =0 
read ret=-1
read string:

mallac  success!
magic 0, datasize =50 len =0 
read ret=-1
read string:
getwrite lock!
write done!
write ret=0
getwrite lock!
write done!
write ret=0
valid_size=58
read ret=50
read string:Never say nothing to do, something worth to try!

getwrite lock!
write done!
write ret=0
getwrite lock!
write done!
write ret=0
valid_size=116
read ret=50
read string:Never say nothing to do, something worth to try!

至此,利用共享内存的非阻塞FIFO已实现。

代码已打包,感兴趣的话可以下载试一下,<^-^>~~

完整源码:

https://download.csdn.net/download/qq_35378417/13090417

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值