linux进程间通信:POSIX 共享内存

思维导图

在这里插入图片描述
之前学习过sysemV 的共享内存的实现及使用原理,参考linux进程间通信:system V 共享内存
POSIX 同样提供共享内存的接口,基本原理和system V的共享内存是一样的。

通信原理
  • 多个进程共享物理内存的同一块区域(通常称之为“段”:segment)
  • 抛弃了内核态消息转存处理的过程,让两个进程直接通过一块内存进行通信

我们普通的像PIPE,FIFO,消息队列等的通信方式如下图:
在这里插入图片描述
这种方式的通信不论读写,都需要内核态(系统调用 read,write,pipe,mkfifo,msgget,msgsnd,msgrcv等)的介入,而且都需要经过数据从虚拟地址空间到物理地址空间的拷贝。
而共享内存的通信方式则都避免了以上的通信问题,直接为两个进程开辟相同的内存空间进行数据交互。
在这里插入图片描述

优势
  • 减少了内存的拷贝(从用户拷贝到内核,从内核拷贝到用户)
  • 减少了2次系统调用(系统调用比较消耗性能,因为CPU处理系统调用时需要从用户态切换到内核态),提高了系统性能
POSIX 共享内存 编程接口

关于共享内存的接口详细使用就不一一描述,可以通过man shm_open这种类似的方式查看具体如何使用接口

//创建共享内存
int shm_open(const char *name, int oflag, mode_t mode);
//当共享内存引用计数为0时,删除共享内存
int shm_unlink(const char *name);
//获取文件相关的信息,将获取到的信息放入到statbuf结构体中
int fstat(int fd, struct stat *statbuf);
//调整文件大小,通过裁剪指定字节达到对文件大小的精准控制
int ftruncate(int fd, off_t length);
//将进程空间的文件映射到内存,也可以将进程空间的匿名区域映射到内存
void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
//解除文件或者匿名映射
int munmap(void *addr, size_t length);

以上接口包含头文件 <sys/mman.h> <sys/mman.h>

编程案例
  1. 共享内存基本使用
    shm_read.c 共享内存的读端

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    
    #define SHM_NAME "/shm"
    
    int main() {
    	int shm_fd;
    	//创建共享内存文件标识符
    	shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);
    	if (shm_fd == -1) {
    		printf("shm_open failed\n");
    	}
    
    	//设置共享内存的文件大小
    	ftruncate(shm_fd , 8192);
    
    	//获取共享内存文件相关属性信息,这里获取的是文件大小
    	struct stat filestat;
    	fstat(shm_fd, &filestat);
    	printf("st_size :%ld\n",filestat.st_size);
    
    	//映射共享内存,并获取共享内存的地址
    	char *shm_ptr;
    	shm_ptr = (char*)mmap(NULL,filestat.st_size,\
    				PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);
    	close(shm_fd);
    	
    	//获取共享内存地址中的内容并打印,最后再解除映射,删除共享内存
    	printf("pid %d:%s\n",getpid(),shm_ptr);
    	munmap(shm_ptr, filestat.st_size);
    	shm_unlink(SHM_NAME);
    	return 0;
    }
    

    shm_write.c共享内存的写端

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    
    #define SHM_NAME "/shm"
    
    int main() {
    	int shm_fd;
    	//创建和读端相同的文件标识
    	shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);
    	if (shm_fd == -1) {
    		printf("shm_open failed\n");
    	}
    	
    	ftruncate(shm_fd , 8192);
    	struct stat filestat;
    	fstat(shm_fd, &filestat);
    	printf("st_size :%ld\n",filestat.st_size);
    
    	char *shm_ptr;
    	shm_ptr = (char*)mmap(NULL,filestat.st_size,\
    				PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);
    	close(shm_fd);
    	
    	//向共享内存中写入数据,这里利用memmove进行内存拷贝写入
    	char buf[] = "hello world";
    	memmove(shm_ptr,buf,sizeof(buf));
    	printf("pid %d:%s\n",getpid(),shm_ptr);
    	//写入完成后解除映射
    	munmap(shm_ptr, filestat.st_size);
    	return 0;
    }
    

    编译gcc shm_read.c -o read -lrt gcc shm_write.c -o write -lrt
    输出如下:
    在这里插入图片描述

  2. 共享内存和信号量一同使用,内存访问的同步
    当读端能够读出的前提是读的时候信号量的value值为1,否则无法读出
    同样写的时候对信号量进行v操作,将信号量的value值加1,为读提供同步条件

    实现如下
    读端sem_shm_read.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <semaphore.h>
    #include <string.h>
    #include <fcntl.h>
    
    #define SHM_NAME "/shm"
    #define SEM_NAME "/memmap_sem"
    
    int main() {
    	//增加信号量的初始创建
    	int shm_fd;
    	sem_t *sem;
    	shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);
    	sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);
    	if (shm_fd == -1 || sem == SEM_FAILED) {
    		printf("open failed\n");
    		_exit(-1);
    	}
    
    	ftruncate(shm_fd , 8192);
    	struct stat filestat;
    	fstat(shm_fd, &filestat);
    	printf("st_size :%ld\n",filestat.st_size);
    
    	char *shm_ptr;
    	shm_ptr = (char*)mmap(NULL,filestat.st_size,\
    				PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);
    	close(shm_fd);
    	
    	//读的时候对信号量做p操作(-1),如果信号量此时为0时则无法读出
    	sem_wait(sem);
    	printf("pid %d:%s\n",getpid(),shm_ptr);
    	sem_close(sem);
    	
    	//读完之后删除共享内存,删除信号量
    	munmap(shm_ptr, filestat.st_size);
    	shm_unlink(SHM_NAME);
    	sem_unlink(SEM_NAME);
    	return 0;
    }
    

    写端shm_write.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <semaphore.h>
    #include <string.h>
    #include <fcntl.h>
    
    #define SHM_NAME "/shm"
    #define SEM_NAME "/memmap_sem"
    
    int main() {
    	int shm_fd;
    	sem_t *sem;
    	shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);
    	sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);
    	if (shm_fd == -1 || sem == SEM_FAILED) {
    		printf("open failed\n");
    		_exit(-1);
    	}
    
    	ftruncate(shm_fd , 8192);
    	struct stat filestat;
    	fstat(shm_fd, &filestat);
    	printf("st_size :%ld\n",filestat.st_size);
    
    	char *shm_ptr;
    	shm_ptr = (char*)mmap(NULL,filestat.st_size,\
    				PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);
    	close(shm_fd);
    
    	char buf[] = "hello world";
    	memmove(shm_ptr,buf,sizeof(buf));
    	printf("pid %d:%s\n",getpid(),shm_ptr);
    
    	//写的时候对信号量的值执行v(+1)操作, 方便后续的读
    	sem_post(sem);
    	sem_close(sem);
    	munmap(shm_ptr, filestat.st_size);
    	return 0;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值