目录
1.共享内存的概念
注意:共享内存是在用户区的
2.共享内存的使用步骤
shmget-->shmat--->shmdt-->shmctl
3.函数介绍
3.1shmget函数进行创建
int shmget(key_t key, size_t size, int shmflg);
- 功能:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识。
新创建的内存段中的数据都会被初始化为0
- 参数:
- key : key_t类型是一个整形,通过这个找到或者创建一个共享内存。
一般使用16进制表示,非0值
- size: 共享内存的大小,size应该按页对齐:由于操作系统中管理物理内存时一般以页面(page)为单位进行划分和管理,
所以建议将size按照页面大小来对齐。例如,在Linux系统中,页面的大小通常是4KB或者8KB。
- shmflg: 属性
- 设置访问权限
- 附加属性:创建/判断共享内存是不是存在
- 创建:IPC_CREAT
- 判断共享内存是否存在: IPC_EXCL , 需要和IPC_CREAT一起使用
IPC_CREAT | IPC_EXCL | 0664
- 返回值:
失败:-1 并设置错误号
成功:>0 返回共享内存的引用的ID,后面操作共享内存都是通过这个值。
注意,key是名字,返回值是身份证号shmid,不是相等的。
3.2shmat函数进行关联
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 功能:和当前的进程进行关联
- 参数:
- shmid : 共享内存的标识(ID),由shmget返回值获取
- shmaddr: 申请的共享内存的起始地址,设置为NULL,内核指定
- shmflg : 对共享内存的操作
- 读 : SHM_RDONLY, 必须要有读权限
- 读写: 0
- 返回值:
成功:返回共享内存的首(起始)地址。 失败(void *) -1
3.3shmdt函数解除关联
int shmdt(const void *shmaddr);
- 功能:解除当前进程和共享内存的关联
- 参数:
shmaddr:共享内存的首地址
- 返回值:成功 0, 失败 -1
3.4shmctl函数操作共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 功能:对共享内存进行操作。删除共享内存,共享内存要删除才会消失,创建共享内存的进程被销毁了对共享内存是没有任何影响。
- 参数:
- shmid: 共享内存的ID
- cmd : 要做的操作
- IPC_STAT : 获取共享内存的当前的状态
- IPC_SET : 设置共享内存的状态
- IPC_RMID: 标记共享内存被销毁
- buf:需要设置或者获取的共享内存的属性信息
- IPC_STAT : buf存储数据,传出参数
- IPC_SET : buf中需要初始化数据,设置到内核中,传入参数
- IPC_RMID : 没有用,NULL
shmctl(id,IPC_RMID,NULL)表示删除
注意:创建共享内存的进程被销毁了对共享内存是没有任何影响。
4.一些常见的问题
1.操作系统如何知道一块共享内存被多少个进程关联?
共享内存维护了一个结构体struct shmid_ds 这个结构体中有一个成员 shm_nattch记录了关联的进程个数
2.可不可以对共享内存进行多次删除 shmctl
可以,因为shmctl只是标记删除,当和共享内存关联的进程数为0时才真正删除
标记后key变为0,表示标记删除,如果一个进程和共享内存取消关联,那么这个进程就不能继续操作这个共享内存。也不能进行关联。标识符发生了变化,表示不能进行新的映射连接
3.共享内存和内存映射的区别
--共享内存可以直接创建,而内存映射需要磁盘文件(匿名映射除外)
--共享内存的效率更高
--共享内存:所有的进程使用一块内存;内存映射:每个进程在自己的虚拟地址空间中有一个独立的内存,他们映射一块磁盘中的内存
--如果进程突然退出:共享内存还存在,内存映射消失
--运行进程的电脑死机:共享内存中的数据消失,内存映射区的数据由于磁盘内存还在,保留
--进程退出,内存映射区销毁;进程退出,共享内存还在,标记删除,如果一个进程退出,会自动和共享内存进行取消关联。
测试代码:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
//write.c
int main() {
// 1.创建一个共享内存
int shmid = shmget(100, 4096, IPC_CREAT|0664);
printf("shmid : %d\n", shmid);
// 2.和当前进程进行关联
void * ptr = shmat(shmid, NULL, 0);
char * str = "helloworld";
// 3.写数据
memcpy(ptr, str, strlen(str) + 1);//放'\0'
printf("按任意键继续\n");
getchar();//等待
// 4.解除关联
shmdt(ptr);
// 5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
//read.c
int main() {
// 1.获取一个共享内存
int shmid = shmget(100, 0, IPC_CREAT);//一般写0表示获取,不能大于设置的大小
printf("shmid : %d\n", shmid);
// 2.和当前进程进行关联
void* ptr = shmat(shmid, NULL, 0);
// 3.读数据
printf("%s\n", (char*)ptr);
printf("按任意键继续\n");
getchar();
// 4.解除关联
shmdt(ptr);
// 5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
注意:在读进程中,int shmid = shmget(100, 0, IPC_CREAT);//一般写0表示获取,不能大于设置的大小