####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次。