Linux-进程间通信(三、共享存储)

####LINUX进程间通信
####一、匿名管道与命名管道
####二、消息队列
####三、共享存储


####共享存储概述:

#####共享存储允许两个或更多进程共享一给定的存储区。共享内存区是最快的IPC形式,一旦这样的内存映射到共享它的进程的 地址空间,这些进程间数据传递不再涉及到内核,也就是说进程不再需要切换到内核态来传递数据。所以比起消息队列一直在用户态和内核态的切换,共享存储更高效。


####相关命令:

ipcs -m :查看当前内核中的共享内存段;
ipcrm -m +shmid :删除对应shmid的共享内存段


####共享内存示意图:
这里写图片描述
#####也就是说:进程同时拿到同一片共享存储区的shmid,使用shmat将对应的共享存储空间连接到自己的进程地址空间,在进行操作完成之后,再使用shmdt断开连接。在整体使用过程中不需要多次进行状态切换。


####共享内存数据结构:
这里写图片描述


####函数介绍:
####一、shmget:获得共享存储标识符

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

       int shmget(key_t key, size_t size, int shmflg);

//返回值:成功返回共享内存的标识符,失败返回-1

#####参数介绍:
#####①key:与消息队列相同,都是由ftok函数生成;
#####②size:共享内存大小(单位/字节);通常将其向上取为系统页长的整数倍;
#####③shmflg:用法和创建文件使用的mode模式标志一样。可参考消息队列中的介绍。


####二、shmctl: 控制共享内存

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

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

//成功返回0,失败返回-1

####参数介绍:
#####①shmid:需要进行操作的共享内存标识码;
#####②cmd:需要进行的操作(分为IPC_STAT,IPC_SET,IPC_RMID)
#####③buf:一个指向shmid_ds结构体类型的结构体指针,当参数为IPC_STAT和IPC_SET时,需要给定一个结构体,用来指定修改的值。


####三、shmat:将共享内存段连接到进程地址空间

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

       void *shmat(int shmid, const void *shmaddr, int shmflg);

//返回值:成功返回指向共享内存第一个字节的指针,失败返回-1

####参数介绍:
#####①shmid:之前由ftok生成的共享内存标识符;
#####②shmaddr:指定连接的地址;
#####③shmflg:可取SHM_RND和SHM_RDONLY

shmaddr取值为NULL,内核自动选择一个地址
shmaddr不为NULL,且shmflg无SHM_RND标记,则以shmaddr为连接地址;
shmaddr不为NULL,且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍
shmflg = SHM_RDONLY:表示连接操作用来只读共享内存。

#####注:更详细的介绍可自行man或查看《unix环境高级编程》

####四、shmdt:将共享内存段与当前进程脱离

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

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);

//返回值:成功返回0,失败返回-1

####参数介绍:
#####shmaddr:由shmat所返回的指针。


####注意:
#####1、共享内存无同步互斥机制!!
#####2、共享内存段随内核,所以我们最好是在进程结束之前不再使用时,就断开与共享内存的连接。


####示例:通过一片共享内存存储区,client向其中写数据,server读取数据并打印到屏幕上。

/**********comm.h*************/
#pragma once

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

#define PATHNAME "."
#define PROJ_ID 0x66

int createShm(int size);

int destroyShm(int shmid);

int getShm(int size);


/**********comm.c**********/
#include "comm.h"

int commShm(int size, int flags)
{
	key_t key = ftok(PATHNAME, PROJ_ID);
	if(key < 0){
		perror("ftok");
		return -1;
	}

	int shmid = shmget(key, size, flags);
	if(shmid < 0){
		perror("shmget");
		return -2;
	}
	return shmid;
}

int destroyShm(int shmid)
{
	if(shmctl(shmid, IPC_RMID, NULL) < 0){
		perror("shmctl");
		return -1;
	}
	return 0;
}

int creatShm(int size)
{
	return commShm(size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(int size)
{
	return commShm(size, IPC_CREAT);
}

/**********client.c********/
#include "comm.h"

int main()
{
	int shmid = getShm(4094);
	char* addr = shmat(shmid, NULL, 0);
	sleep(1);
	int i = 0;
	while(i < 5){
		addr[i] = 'A' +i;
		i++;
		addr[i] = 0;
		sleep(1);
	}
	shmdt(addr);
	sleep(2);
	return 0;
}


/*********server.c*******/
#include "comm.h"

int main()
{
	int shmid = creatShm(4096);
	
	char* addr = (char*)shmat(shmid, NULL, 0);
	sleep(5);
	int i = 0;
	while(i < 10){
		i++;
		printf("client> %s\n",addr);
		sleep(1);
	}

	shmdt(addr);
	sleep(2);
	destroyShm(shmid);
	return 0;
}



/*******Makefile********/
.PHONY : all
all : server client

server : server.c comm.c
	gcc $^ -o $@

client : client.c comm.c
	gcc $^ -o $@

.PHONY : clean
clean:
	rm server client


#####运行结果:
#####打开server端,再打开client端,经过sleep后,client端向共享内存中写入数据,server端不断读取,但是当client端停止写入时,共享内存区的数据还是最后一次写入的数据,所以又多输出5次。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值