共享内存区域是被多个进程共享的一部分物理内存;如果多个进程都把该内存区域映射到自身的虚拟地址空间,则这些进程都可以直接访问该共享内存区域,从而可以通过该区域进行通信。
共享内存是进程间共享数据最快的一种方法,一个进程向共享内存区域写入了数据,共享这个内存区域所有进程就可以立刻查看到其中的内容;这快共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中;但是它不需要在所有的进程的虚拟内存中都有相同的虚拟地址。
如下图所示:
像所有的System V IPC对象一样,对于共享内存的获取是由Key控制。内存共享之后,对进程如何使用这块内存就不再做检查。它们必须依赖于其他机制。
每一个新创建的共享内存对象都有一个shmid_kernel数据结构来表达,系统中所有的shmid_kernel数据结构都保存在shm_segs向量表中,该向量表的每一个元素都是一个指向shmid_kernel数据结构的指针。
shm_segs向量表的定义如下:
struct shmid_kernel *shm_segs【SHMMNI】;其中SHMMNI为128,表示系统中最多有128个共享内存对象。
数据结构如下:
struct shmid_kernel
{
struct shmid_ds u;
unsigned long shm_npages;
unsigned long *shm_pages;
struct vm_area_struct *attaches;
};
其中:
shm_pages代表该共享内存对象所占据的内存页面组数,组数里面的每一个元素当然是每个内存页面的起始地址。
shm_npages则是该共享内存对象占用内存页面的个数,以页为单位,这个数量涵盖了申请空间的最小倍数。
shmid_ds是一个数据结构,描述了该共享内存区的认证信息,其结构如下:
struct shmid_ds
{
struct ipc_perm shm_perm;
int shm_segsz;
_kernel_time_t shm_atime;
_kernel_time_t shm_dtime;
_kernel_time_t shm_ctime;
_kernel_ipc_pid_t shm_cpid;
_kernel_ipc_pid_t shm_lpid;
unsigned short shm_nattch;
unsigned short shm_unused;
void *shm_unused2;
void *shm_unused3;
};
attaches 描述被该共享的物理内存对象所映射的各个进程的虚拟内存区域。
Linux共享内存提供了4种操作:
1.共享内存对象的创建
int sys_shmget(key_t key,int size,int shmflg);
这里的key表示该共享内存对象的键值,size是该共享内存大小以字节为单位,shmflg是标志。
它所做的工作如下:
1)如果key==IPC_PRIVATE,则总是会创建一个新的共享内存对象。
2)在向量表shm_segs中查找键值key的共享内存对象,确认其合法性
2.关联,在创建某个共享内存区域的标识符后,还必须将共享内存区域映射到进程的虚拟地址空间,然后才能使用该共享内存区域,系统调用sys_ipc(call值SHMAT)用于共享内存区域到进程虚拟地址空间的映射,而真正完成动作的函数是sys_shmat,其定义如下:
void *shmat(int shmid,const void *shmaddr,int shmflg);
其所做的工作是:
1)根据shmid找到共享内存对象
2)如果shmaddr为0,即用户没有指定该共享内存区域在它的虚拟空间中的位置,则由系统在进程的虚拟地址空间中为其找一块区域,否则就用shmaddr作为映射的虚拟地址。
3)检查虚拟地址的合法性
4)认证检查
5)申请一块内存用于建立数据结构vm_area_struct,填写该结构
6)检查该内存区域,将其加入到进程mm结构和该共享内存对象的vm_area_struct队列中
3.分离,当进程不再需要共享虚拟内存的时候,它们与之分离;系统调用sys_ipc(call 值为shmdt)用于该共享内存与进程虚拟地址空间的分离,而真正完成分离动作的函数是 int sys_shmdt(char *shmaddr);
4.控制,Linux在共享内存上实现第四种操作是共享内存控制,其由函数sys_shmctl实现;共享内存提供了一种快速灵活的机制,它允许进程之间直接共享大量数据,而无需拷贝使用或系统调用。