使用shm_open遇到的问题

最初的设计:
有A、B、C三个进程使用共享内存shm_file。
在进程拉起时按照A->B->C的顺序拉起。
A进程使用

#define SHM_SIZE 1000
app_init({
	int shm_id =0;
	shm_unlink("/shm_file");
	shm_id  = shm_open("/shm_file", O_CREAT | O_EXCL |O_RDWR, 0644);
	if(shm_id  < 0)
	{
			printf("create error\n");
			 return -1;
     }
     if(0 != ftruncate(shm_id, SHM_SIZE ))
     {
			printf("ftruncate error\n");
			 return -1;
     }
     char addr *=mmap(0, SHM_SIZE, PORT_READ |PORT_WRITE,MAP_SHARED,shm_id,0);
     if(addr == (void*)-1)
     {
            close(shm_id);
              return -1;
      }
      close(shm_id);
}

B 和C使用共享内存:

lib_init()
{
	int shm_id =0;
	shm_id  = shm_open("/shm_file", O_RDWR, 0644);
	if(shm_id  < 0)
	{
			printf("create error\n");
			 return -1;
     }     
     char addr *=mmap(0, SHM_SIZE, PORT_READ |PORT_WRITE,MAP_SHARED,shm_id,0);
     if(addr == (void*)-1)
     {
            close(shm_id);
              return -1;
      }
      close(shm_id);
}’

这样的使用方式有两个局限:
(1)A进程虽然最先启动,但是A进程还没运行到app_init()的时候B进程先运行了lib_init(),并且往共享内存中写入数据,之后A进程才运行app_init(), 这时候使用shm_unlink把共享内存的数据删掉了,导致数据丢失。–这个也许可以通过信号量来解决时序问题。
(2)在不同是设备上部署的是A、B、C的不同排列组合。比如在设备类型1中可能部署A和B,在设备类型2中部署B和C,在设备类型3中部署A和C,但是代码是共用一套,无法固定哪个进程来使用app_init—除非使用宏隔开。

为了解决上述问题,让进程A、B、C使用相同的共享内存初始化接口lib_init(),主要修改点在于第一个启动的进程创建共享内存,其他的进程使用共享内存:

lib_init({
	int shm_id =0;
	shm_id  = shm_open("/shm_file", O_CREAT | O_EXCL |O_RDWR, 0644);
	if(shm_id  < 0)
	{	
	 shm_id  = shm_open("/shm_file", O_RDWR, 0644);if(shm_id  < 0)
	 {
			 printf("create error\n");
			 return -1;
			 } 
     }   
	 else
     {
	     if(0 != ftruncate(shm_id, SHM_SIZE ))
	     {
				printf("ftruncate error\n");
				 return -1;
	     }
     } 
     char addr *=mmap(0, SHM_SIZE, PORT_READ |PORT_WRITE,MAP_SHARED,shm_id,0);
     if(addr == (void*)-1)
     {
            close(shm_id);
              return -1;
      }
      close(shm_id);
}

但是上述方法还存在问题:A, B和C,第一个启动的进程会创建共享内存并使用ftruncate设置共享内存大小,后面的进程判断共享内存已创建就会直接mmap并使用共享内存,但是当前共享内存可能正在被第一个进程ftruncate,导致后面的进程在使用共享内存时出现Bus error并退出。
3.针对上述问题,加上了信号量的使用:sem_wait()/sem_post()/sem_create()是信号量的实现

lib_init({
	int shm_id =0;
	shm_id  = shm_open("/shm_file", O_CREAT | O_EXCL |O_RDWR, 0644);
	if(shm_id  < 0)
	{	
		 shm_id  = shm_open("/shm_file", O_RDWR, 0644);if(shm_id  < 0)
		 {
				 printf("create error\n");
				 return -1;
				 } 
		 }   
		 sem_wait();
	 else
     {
		 sem_create(); 
	     if(0 != ftruncate(shm_id, SHM_SIZE ))
	     {
				printf("ftruncate error\n");
				 return -1;
	     }
		 sem_post();
     } 
     char addr *=mmap(0, SHM_SIZE, PORT_READ |PORT_WRITE,MAP_SHARED,shm_id,0);
     if(addr == (void*)-1)
     {
            close(shm_id);
              return -1;
      }
      close(shm_id);
}

本以为能完美的解决问题,没想到还有绝杀,有个服务在启动时rm /dev/shm/*,shm_open()创建的文件就在这个目录下,这就导致A、B、C已经正常运行了,然后被执行了rm /dev/shm/*之后,如果有个D进程也使用了lib_init()接口(D是维测进程,为了打印出共享内存中的维测信息),这时候D会认为共享内存没创建,重新创建共享内容,这样A、B、C写入的数据都没有了。
尴尬的是shm_open()创建的文件只能在/dev/shm下,尝试使用文件链接的方式,但是/dev/shm下的东西被清空,共享内存还是用不了。

4.使用另一种共享内存接口
可以指定key或者随机生成key
随机生成key,示例:

#define
char filename[128] = {0};
snprintf(filename, 128,"/tmp/myshm");
FILE *file = fopen(filename, "w");
if(!file)
{
	printf("open file");
	return -1;
}
key_t key = ftok(pathname,66);
if(key == -1){
	printf("ftok failed");
	return -1;
}

也可以指定key

#define SHM_SIZE 1000
lib_init({
		char *addr = NULL;
		int shmid = shmget(MEM_KEY, SHM_SIZE , 0666 | IPC_CREAT | IPC_EXCL);
		if(shmid > 0)
		{
        	  addr = shmat(shmid,NULL,0);
        	  if(addr == (void*)-1)
        	  {
        	  			goto shmat_err;
        	  }
        	     memset(addr,0,SHM_SIZE );
         }
         else if(shmid == -1 && errno == EEXIST)
         {
         		 printf("key already exist\n");
         		    shmid == shmget(MEM_KEY, SHM_SIZE, 0666);
         		       if(shmid < 0)
         		                goto shmget_err;
         		       addr = shmat(shmid, NULL,0);
         		       if(addr == (void*)-1)
		        	  {
		        	  			goto shmat_err;
		        	  }
         }
         else
         {
         		 goto shmat_err;
         }
         return 0;
shmget_err:
		return -1;
shmat_err:
         return -1;
}

这种方式创建的共享内存在文件系统上没有对应的文件,但是也防不住通过在脚本中ipcs -m  查看系统共享内存信息,然后通过ipcrm -M key的方式删除共享内存,删除之后代码读出来的也是空的。

5、还是使用open()+mmap()的方式。

lib_init({
	int shm_id =0;
	shm_id  = open("/tmp/shm_file", O_CREAT | O_EXCL |O_RDWR, 0644);
	if(shm_id  < 0)
	{	
		 shm_id  = open("/tmp/shm_file", O_RDWR, 0644);if(shm_id  < 0)
		 {
				 printf("create error\n");
				 return -1;
				 } 
		 }   
		 sem_wait();
	 else
     {
		 sem_create(); 
	     if(0 != ftruncate(shm_id, SHM_SIZE ))
	     {
				printf("ftruncate error\n");
				 return -1;
	     }
		 sem_post();
     } 
     char addr *=mmap(0, SHM_SIZE, PORT_READ |PORT_WRITE,MAP_SHARED,shm_id,0);
     if(addr == (void*)-1)
     {
            close(shm_id);
              return -1;
      }
      close(shm_id);
}

open()可以指定文件在任何目录,但是shm_open的文件一定在/dev/shm目录,open()方式和shm_open()有什么区别呢?一下内容来自ds的回答:
我理解是如果是进程间要共享数据,使用open+mmap或者shm_open+mmap都可以,但是如果是自己一个进程存储临时数据要用到匿名内存,就用shm_open
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值