共享内存的接口均在#include
头文件中
int shmget(key_t key,size_t size,int shmflg)
参数:
- key:共享内存的标识符,用来标识一块共享内存,在操作系统中,共享内存的标识是不能重置的。可以直接给一个32位的16进制的数字。
- size:共享内存的大小
- shmflg:
① IPC_CREAT:如果key标识的共享内存不存在,则创建。
② IPC_EXEC | IPC_CREAT:如果key标识的共享内存存在,则保存。该参数通常是要搭配IPC_CREAT一起使用。
③ 权限:需要按位或上一个8进制的数字
返回值:
查看共享内存的命令:ipcs m
void shmat(int shmid, const void* shmaddr, int shmflg)
参数:
- shmid:共享内存的操作句柄,shmget的返回值
- shmaddr:将共享内存附加到shmaddr,一般情况下,都不会自己去指定映射到共享区当中的那一块虚拟地址,而是传递NULL值,让操作系统去选择
- shmflg:标志着将共享内存附加到进程之后,进程对共享内存的读写属性
- 0:读写失败
- SHM_RDONLY:只读
返回值:
int shmdt(const void* shmaddr)
参数:
- shmaddr:调用shmat函数获取到的共享内存的虚拟地址
int shmctl(int shmid,int cmd,struct shmid_ds* buf)
该函数可以类比获取 / 设置文件描述符属性的fcntl
函数。
参数:
shmid:共享内存的操作句柄
cmd:
①IPC_STAT:获取共享内存参数
②IPC_SET:设置共享内存的属性
③IPC_RMID:删除共享内存struct shmid_ds:是共享内存的属性对应的结构体。具体定义如下:
参数cmd的使用:
① IPC_STAT:获取共享内存参数
struct shmid_ds tmp;
shmctl(shmid,IPC_STAT,&tmp);
②IPC_SET:设置共享内存的属性
struct shmid_ds tmp;
tmp.segsz = 1024;
shmctl(shmid,IPC_SET,&tmp);
③IPC_RMID:删除共享内存
shmctl(shmid,IPC_RMID,NULL);
我们可以创建两个文件testA.c和testB.c,利用shmget函数创建出共享内存,然后testA.c在共享内存中写入"It’s test to share memory,I am testA ",然后在testB.c中对共享内存中的数据进行读,并将其输出在屏幕上,规定共享内存的标识符为0x86868686。
代码如下:
testA.c
#include
#include
#include
#define key 0x86868686
int main()
{
puts("start to testA to write shm");
int shmid = shmget(key,1024,IPC_CREAT | 0664);
void* addr = shmat(shmid,NULL,0);
sprintf((char*)addr,"It's test to share memory,I am testA");
puts("end to testA to write shm");
return 0;
}
碎片知识:在往共享内存中写数据的时候,可以用strncpy、sprintf、memcpy
testB.c
#include
#include
#include
#define key 0x86868686
int main()
{
puts("start to testB to read shm");
int shmid = shmget(key,1024,IPC_CREAT | 0664);
void* addr = shmat(shmid,NULL,0);
printf("%s\n",(char*)addr);
puts("end to testB to read shm");
return 0;
}
运行结果:
使用ipcs -m
查看刚才的共享内存
删除共享内存:
方法一:在文件中使用shmctl函数
删除。
方法二:在命令行中使用ipcrm -m [shmid]
命令进行删除
- 共享内存的生命周期跟随操作系统的内核
- 共享内存的读取,采用的是拷贝读,而不是拿走读(这是和管道的一个本质区别)
- 共享内存的写是覆盖写
那么问题来了,如果删除一个被进程附加的共享内存,会发生什么情况?
操作:让testA程序不结束,一直运行,然后删除其对应的共享内存
现象:
并且当,testA程序结束后,该共享内存就会被释放。
本质:
我们能通过
ipcs -m
查看当前被删除共享内存的信息,说明在操作系统内核,描述该内存的结构体没有被释放,但是共享内存所使用的空间已经被释放掉了,所以,附加的进程如果再次操作共享内存就会面临崩溃的风险。
① 消息队列首先是一个队列,具有先进先出的性质。
② 消息队列的本质上也是内核当中维护的一个双向链表,但是满足了先进先出的特性,所以被称为队列。
③ 消息队列当中的消息,带有类型的数据,类型和类型之间是有优先级的。
④消息队列可以获取同种类的,距离队首最近的元素。
消息队列的接口均在#include
头文件中
int megget(key_t key,int msgflg)
参数:
- key:消息队列的标识符
- msgflg:IPC_CREAT,……,同shmget中的属性
返回值:
返回消息队列的操作句柄
int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg);
参数:
- msqid:消息队列的操作句柄
- msgp:要发送到消息队列的信息(和shmctl 函数的struct shmid_ds一样)该信息是一个消息队列的结构体
①long mtype
:一定要大于0,它表示的是消息的类型
②char mtext[1]
:发送的数据只是一个类型,程序员在发送数据的时候,传递的结构体中一定要有一个变量为long的类型,表示消息的优先级,至于小的数据,程序员可以自己指定。
- msgsz:指定发送的数据大小,只计算自己发送的数据大小,不算long类型
- msgflg:
① IPC_NOWAIT:非阻塞的发送方式
② 0 :阻塞发送
返回值:
成功发送0,失败返回-1.
碎片知识:long类型在Linux中32位OS下是4Byte,64位OS下是8Byte,64位Windows OS下是4Byte。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数:
msqid:消息队列的操作句柄
msgp:最大接收能力(即最大能接收多大)
msgsz:
① >0:表示获取队列中距离队首最近的同类型的元素
② ==0:直接获取队首的元素
③ <0:详见下方解释msgflg:同上msgsnd函数的属性
详解msgsz参数的 < 0 属性:
① 需要将 < 0 的msgtype的值取绝对值
② 从队首一直到|msgtype|
区间的消息全部过滤出来
③ 从区间中获取和绝对值msgtype一样的消息,若是没有获取到,则进行步骤④
④ 在区间中取类型最小的消息
举个例子:
例子1:若传入的msgsz是 - 5,且消息队列中的消息是[1,2,3,4,5,6,7,8,9]。
由于msgsz < 0,则过滤消息队列中 -5 到 5 这个区间的消息,即[1,2,3,4,5],然后获取和绝对值一样的消息 5。
例子2:若传入的msgsz是 - 5,且消息队列中的消息是[1,2,3,4,6,7,8,9]。
由于msgsz < 0,则过滤消息队列中 -5 到 5 这个区间的消息,即[1,2,3,4,6],但是不存在和绝对值相同的消息,那么就获取该消息队列中的最小值 1
碎片知识:System V标准是不支持跨平台的,而Posix标准是支持跨平台的