/*
创建一个共享内存区
key值可以由ftok()函数生成,是唯一的进程资源
*/
int main()
{
int shmid;
char *addr;
shmid = shmget(1002, 128, IPC_CREAT|IPC_EXCL|0666);
addr = (char* )shmat(shmid, NULL, 0);
strcpy(addr,"hello world");
return 0;
}
/*
IPC_CREAT 创建一个新的片段。如果没有,检查其权限
IPC_EXCL 这个标志位可以|一起使用
其他几个标志位可以 用命令:man 2 查看
*/
int ret = shmget(1001,4096,IPC_CREAT|IPC_EXCL|0666);
$ ipcs
前段时间被一家公司问到了,一个是内存映射的原理,一个是共享内存的原理,图片来源:人人都是极客。
struct shmid_kernel 用来管理共享内存的结构体
里面记录了创建的PID,以及操作的时间戳,已使用空间和未使用空间
共享内存最多能有128个
shmget函数,首先要查看
- findkey,查找key值得共享内存是否被创建
- findkey(),返回共享内存在
shm_segs
的数组索引 - 找到了就直接返回标识符,没找到就
newseg()
创建 - 创建一个新的
struct shmid_kernel
结构体出来
shmat()
函数用于将共享内存映射到本地虚拟内存地址。
这时一个问题就出来了,共享内存是需要经历一道手续的,内存映射。
共享内存和内存映射的区别。
找到一个可用的虚拟内存地址,如果在调用 shmat()
函数时没有指定了虚拟内存地址,那么就通过 get_unmapped_area()
函数来获取一个可用的虚拟内存地址。
共享内存需要找到虚拟内存地址,然后再进行映射。
如果没有找到,就会引发缺页异常,再度令Mmap去映射一块物理地址实现共享内存。
简而言之,就是不同虚拟地址的进程映射到了同一片内存空间,也减少了磁盘IO的次数。最常见的就是epoll,底层有红黑树
key管理文件描述符,value管理注册的事件
双向链表管理就绪的文件描述符,用于给用户返回轮询
mmap用于传递文件描述符,避免在内核态陷入时间过久,而且每次轮询和就绪都无需再向红黑树重新注册,又避免了频繁陷入用户态和内核态的上下文切换,是支撑服务器百万并发的关键。
只是共享内存也有缺点,毕竟会造成物理地址空间对运行内存的占用,如果系统觉得不够用,会将一部分不常用的运行内存的地址迁移到物理地址,又会涉及到地址交换技术。