进程间的通讯:共享内存


前言

共享内存就是允许两个不相关的进程访问同一个逻辑内存
共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。所以不涉及缓冲区大数据拷贝,效率很高。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量
共享内存也有两套机制:System V 和 Posix
在这里插入图片描述


一、System V 共享内存shmget()

#include <sys/ipc.h>
#include <sys/shm.h>

1、创建或打开 shmget()

int shmget(key_t key, size_t size, int shmflg);
success:返回标志符     error:  -1

key :程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,用于后续的共享内
存函数。
size :以字节为单位指定需要共享的内存容量。
Shmflg:权限码 加上 | 0666 作为校验
	IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
    IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则打开操作。
    IPC_EXCL    只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误
   如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么
返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将
返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并
没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打
开已有的对象
https://www.cnblogs.com/jkred369/p/6733141.html

2、使用 shmat() / 分离shmdt()

①、标题使用 shmat()

第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。

void *shmat(int shmid, const void *shmaddr, int shmflg);
success:一个指向共享内存第一个字节的指针     error:  -1

shm_id:是由shmget函数返回的共享内存标识。
shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,
		  表示让系统来选择共享内存的地址。
shm_flg:是一组标志位,通常为0

②、分离shmdt()

该函数用于将共享内存从当前进程中分离,注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。

int shmdt(const void *shmaddr);
success:0     error:  -1

shmaddr:是shmat函数返回的地址指针。

3、控制操作(删除) shmctl()

int shmctl(int shm_id, int command, struct shmid_ds *buf);

shm_id:shm_id是shmget()函数返回的共享内存标识符。

command:采取的操作,它可以取下面的三个值 :
		IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,
				  即用共享内存的当前关联值覆盖shmid_ds的值。
		IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds
				 结构中给出的值
		IPC_RMID:删除共享内存段
buf: 一个结构指针,它指向共享内存模式和访问权限的结构

4、共享内存内放置posix无名信号量

实现一个写一个收
shmdata.h

#include <semaphore.h>

#define TEXT_SZ 64
#define SHMID 0x1236


struct shared_use_st  
{  
	sem_t sem_wait_to_read;
	sem_t sem_wait_to_write;
	char text[TEXT_SZ];//记录写入和读取的文本  
};  

write.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/shm.h> 
#include <sys/shm.h> 
#include "shmdata.h"
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int running = 1;  
    struct shared_use_st *shared = NULL;  
    char buffer[TEXT_SZ + 1];//用于保存输入的文本  
    int shmid;  

    //创建共享内存  
    shmid = shmget((key_t)SHMID, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
         fprintf(stderr, "shmget failed\n");  
         return -1;
    }  

    //将共享内存连接到当前进程的地址空间  
    //成功情况下shm指向该共享内存首地址
    shared = (struct shared_use_st*)shmat(shmid, (void*)0, 0);  
    if(shared == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        return -1;
    }  
    printf("Memory attached at %p\n", shared);  

    sem_init(&shared->sem_wait_to_write, 1, 1); 
    sem_init(&shared->sem_wait_to_read, 1, 0); 

    memset(shared->text, 0, sizeof(shared->text));	
	
    while(running)
    {  
  		sem_wait(&shared->sem_wait_to_write);
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        strncpy(shared->text, buffer, TEXT_SZ);  		
		sem_post(&shared->sem_wait_to_read);

        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
    }  
    //把共享内存从当前进程中分离  
    if(shmdt(shared) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        return -1;  
    }  

	 //删除共享内存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
        return -1;
    }  
    	
    sleep(2);  

   return 0;  
}

read.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/ipc.h>   
#include <sys/shm.h> 
#include "shmdata.h"
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int running = 1;//程序是否继续运行的标志  
    struct shared_use_st *shared;//指向shm  
    int shmid;//共享内存标识符  

    //创建共享内存  
   // shmid = shmget((key_t)SHMID, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
	shmid = shmget((key_t)SHMID, 0, 0); 
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        return -1;
    }  
    //将共享内存连接到当前进程的地址空间  
    shared = (struct shared_use_st*)shmat(shmid, 0, 0);  
    if(shared == NULL)  
    {  
        fprintf(stderr, "shmat failed\n");  
        return -1;
    }  
    printf("\nMemory attached at %p\n", shared);  

    while(running)//读取共享内存中的数据  
    {  
		sem_wait(&shared->sem_wait_to_read);	
		printf("You read: %s", shared->text);  
		sleep(rand() % 3); 		
		sem_post(&shared->sem_wait_to_write);
		
		if(strncmp(shared->text, "end", 3) == 0)  
		    running = 0;           
    }  
    sem_destroy(&shared->sem_wait_to_read);
    sem_destroy(&shared->sem_wait_to_write);

    //把共享内存从当前进程中分离  
    if(shmdt((void *)shared) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        return -1;
    }  
    //删除共享内存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
        return -1;
    }  

    return 0;   
}

在这里插入图片描述
参考https://www.cnblogs.com/52php/p/5861372.html

二、Posix 共享内存shm_open()

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
Link with -lrt.

1、创建/打开 shm_open()+ftruncate()

①第一步先shm_open()创建内存对象

int shm_open(const char *name, int oflag, mode_t mode);
success: 返回描述符      error: -1

name:参数标识出了待创建或待打开的共享内存对象
oflag:
	Access Mode:
			O_RDONLY	以只读的方式打开共享内存对象
			O_RDWR	    以读写的方式打开共享内存对象
	Opening-time flags(Bitwise Or):
			O_CREAT     表示创建共享内存对象,刚被创建的对象会被初始化为0byte可
						以使用ftuncate()调整大小
			O_EXCL		用来确保共享内存对象被成功创建,如果对象已经存在,那么返
						回错误
			O_TRUNC		表示如果共享内存对象已经存在那么把它清空
mode: 访问权限 0666

②第二步设置/修改共享内存大小ftruncate()

int ftruncate(int fd, off_t length);
success:0        error:-1

fd: 描述符  shm_open()返回值
length:需要设置的大小
		如果原文件大小>指定大小,原文件中多余的部分会被截除

2、将内存对象映射到进程mmap()

void *mmap(void *addr, size_t length, int prot, int flags, 
									int fd, off_t offset);
success:0        error:0

addr:映射的起始地址, 如果为NULL则由kernel自行选择--->最合适的方法
length:映射的区域长度
prot:映射内存的保护权限	
					PROT_EXEC表示映射的内存页可执行
					PROT_READ表示映射的内存可被读
					PROT_WRITE表示映射的内存可被写
					PROT_NONE表示映射的内存不可访问	
flags:		
	MAP_SHARED:表示共享这块映射的内存,读写这块内存相当于直接读写文件,这些操作对其
			   他进程可见,由于OS对文件的读写都有缓存机制,所以实际上不会立即将更改
			   写入文件,除非带用msync()mumap()
	MAP_PRIVATE:表示创建一个私有的copy-on-write的映射, 更新映射区对其他映射到这
			    个文件的进程是不可见的			
fd:     文件描述符 为shm_open()的返回值
offset: 文件中的偏移量	
eg:
	void* p ;
	p = mmap(NULL,sizeof(struct shared_use_st),PROT_READ|PROT_WRITE,
												MAP_SHARED,fd,0);
	if(MAP_FAILED == p)
		perror("mmap");		    		

3、关闭映射/删除内存对象 munmap() + shm_unlink()

①、关闭文件或设备对内存的映射munmap()


int munmap(void *addr, size_t length);
success:0        error:-1

addr:   mmap()返回值地址
length: 大小

②、删除内存对象shm_unlink()

int shm_unlink(const char *name);
success:0        error:-1

name:内存对象名称

4、共享内存里存放posix无名信号量

write.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/ipc.h>   
#include <sys/shm.h> 
#include <unistd.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include<fcntl.h>
#include "shmdata.h"

int main()
{
    int fd;
    int running = 1; 
    struct shared_use_st *shared = NULL;  
    char buffer[TEXT_SZ + 1];//用于保存输入的文本  
    struct stat filestat;
    
    fd = shm_open(SHARENAME,O_RDWR|O_CREAT,0666);   //创建共享内存对象
    if(fd<0)
    {
        printf("shm_open error\n");
    }
    
    if(ftruncate(fd,sizeof(struct shared_use_st))==-1)    //改变对象大小
    {
        printf("ftruncate error\n");
    }
    
    shared = ( struct shared_use_st *)mmap(NULL,sizeof(struct shared_use_st),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  //内存映射
    if(shared==MAP_FAILED)
    {
        printf("mmap error\n");
    }
    memset(shared->text, 0, sizeof(shared->text));
    sem_init(&shared->sem_wait_to_write, 1, 1); 
    sem_init(&shared->sem_wait_to_read, 1, 1); 
    
    printf("\nMemory attached at %p\n", shared);
    while(running)
    {
		sem_wait(&shared->sem_wait_to_write);		
		printf("Enter some text: ");  
		fgets(buffer, BUFSIZ, stdin);  
		strncpy(shared->text, buffer, TEXT_SZ);  		
		sem_post(&shared->sem_wait_to_read);
				
		if(strncmp(buffer, "end", 3) == 0)  
		   running = 0;  
    }
    munmap(shared,sizeof(struct shared_use_st));
   
    return 0;

}

read.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/ipc.h>   
#include <sys/shm.h> 
#include <unistd.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include<fcntl.h>
#include "shmdata.h"
#include <errno.h>
int main(int argc, char *argv[])
{ 
    int running = 1;//程序是否继续运行的标志  
    struct shared_use_st *shared;//指向shm    
    int fd;
    struct stat sb;
   
    fd = shm_open(SHARENAME,O_RDONLY,0666);   //创建共享内存对象
    if(fd<0)
    {
        printf("shm_open error\n");
    }  
    
    if(fstat(fd,&sb)==-1)
    {
        printf("fstat error\n");
	return -1;
    }   
    //将共享内存连接到当前进程的地址空间 
    shared = (struct shared_use_st*)mmap(NULL,sb.st_size,PROT_READ,MAP_SHARED,fd,0);   
    if(shared == NULL)  
    {  
        printf( "mmap failed\n");  
        return -1;
    }  
    printf("Memory attached at %p\n", shared);  

    while(running)//读取共享内存中的数据  
    {  
		sem_wait(&shared->sem_wait_to_read);
		printf("You read: %s", shared->text);  
		sleep(rand() % 3); 			
		sem_post(&shared->sem_wait_to_write);
		
		if(strncmp(shared->text, "end", 3) == 0)  
			running = 0;        
    }  
    sem_destroy(&shared->sem_wait_to_read);
    sem_destroy(&shared->sem_wait_to_write);

    munmap(shared,sb.st_size);
    shm_unlink(SHARENAME);

    return 0;
    
}

read.c 运行时 sem_wait一直段错误 分配的共享内存没问题 信号量获取失败?????????? 使用有名的就可以 system v共享内存和posix无名信号量也可以


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值