shmget物理内存_共享内存的常用函数详解shmget shmat

共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中。但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址。

象所有的 System V IPC对象一样,对于共享内存对象的获取是由key控制。内存共享之后,对进程如何使用这块内存就不再做检查。它们必须依赖于其它机制,比如System V的信号灯来同步对于共享内存区域的访问(信号灯如何控制对临界代码的访问另起一篇说话)。

每一个新创建的共享内存对象都用一个shmid_kernel数据结构来表达。系统中所有的shmid_kernel数据结构都保存在shm_segs向量表中,该向量表的每一个元素都是一个指向shmid_kernel数据结构的指针。

shm_segs向量表的定义如下:

struct shmid_kernel *shm_segs[SHMMNI];

SHMMNI为128,表示系统中最多可以有128个共享内存对象。

数据结构shmid_kernel的定义如下:struct shmid_kernel

{

struct shmid_ds u;

unsigned long shm_npages;

unsigned long *shm_pages;

struct vm_area_struct *attaches;

};

其中:

shm_pages代表该共享内存对象的所占据的内存页面数组,数组里面的每个元素当然是每个内存页面的起始地址.

shm_npages则是该共享内存对象占用内存页面的个数,以页为单位。这个数量当然涵盖了申请空间的最小整数倍.

(A new shared memory segment,  with size  equal to the value of size rounded up to a multiple of PAGE_SIZE)

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描述被共享的物理内存对象所映射的各进程的虚拟内存区域。每一个希望共享这块内存的进程都必须通过系统调用将其关联(attach)到它的虚拟内存中。这一过程将为该进程创建了一个新的描述这块共享内存的vm_area_struct数据结构。创建时可以指定共享内存在它的虚拟地址空间的位置,也可以让Linux自己为它选择一块足够的空闲区域。

这个新的vm_area_struct结构是维系共享内存和使用它的进程之间的关系的,所以除了要关联进程信息外,还要指明这个共享内存数据结构shmid_kernel所在位置; 另外,便于管理这些经常变化的vm_area_struct,所以采取了链表形式组织这些数据结构,链表由attaches指向,同时 vm_area_struct数据结构中专门提供了两个指针:vm_next_shared和 vm_prev_shared,用于连接该共享区域在使用它的各进程中所对应的vm_area_struct数据结构。

Linux为共享内存提供了四种操作。

1. 共享内存对象的创建或获得。与其它两种IPC机制一样,进程在使用共享内存区域以前,必须通过系统调用sys_ipc (call值为SHMGET)创建一个键值为key的共享内存对象,或获得已经存在的键值为key的某共享内存对象的引用标识符。以后对共享内存对象的访问都通过该引用标识符进行。对共享内存对象的创建或获得由函数sys_shmget完成,其定义如下:

int sys_shmget (key_t key, int size, int shmflg)

这里key是表示该共享内存对象的键值,size是该共享内存区域的大小(以字节为单位),shmflg是标志(对该共享内存对象的特殊要求)。

它所做的工作如下:

1) 如果key == IPC_PRIVATE,则总是会创建一个新的共享内存对象。

但是  (The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more clearly show its function)    * 算出size要占用的页数,检查其合法性。

* 申请一块内存用于建立shmid_kernel数据结构,注意这里申请的内存区域大小不包括真正的共享内存区,实际上,要等到第一个进程试图访问它的时候才真正创建共享内存区。

* 根据该共享内存区所占用的页数,为其申请一块空间用于建立页表(每页4个字节),将页表清0。

* 搜索向量表shm_segs,为新创建的共享内存对象找一个空位置。

* 填写shmid_kernel数据结构,将其加入到向量表shm_segs中为其找到的空位置。

* 返回该共享内存对象的引用标识符。

2) 在向量表shm_segs中查找键值为key的共享内存对象,结果有三:

* 如果没有找到,而且在操作标志shmflg中没有指明要创建新共享内存,则错误返回,否则创建一个新的共享内存对象。

* 如果找到了,但该次操作要求必须创建一个键值为key的新对象,那么错误返回。

* 否则,合法性、认证检查,如有错,则错误返回;否则,返回该内存对象的引用标识符。

共享内存对象的创建者可以控制对于这块内存的访问权限和它的key是公开还是私有。如果有足够的权限,它也可以把共享内存锁定在物理内存中。

参见include/linux/shm.h

2. 关联。在创建或获得某个共享内存区域的引用标识符后,还必须将共享内存区域映射(粘附)到进程的虚拟地址空间,然后才能使用该共享内存区域。系统调用 sys_ipc(call值为SHMAT)用于共享内存区到进程虚拟地址空间的映射,而真正完成粘附动作的是函数sys_shmat,

其定义如下:

#include #include

void *shmat(int shmid, const void *shmaddr, int shmflg);

其中:

shmid是shmget返回的共享内存对象的引用标识符;

shmaddr用来指定该共享内存区域在进程的虚拟地址空间对应的虚拟地址;

shmflg是映射标志;

返回的是在进程中的虚拟地址

该函数所做的工作如下:

1) 根据shmid找到共享内存对象。

2) 如果shmaddr为0,即用户没有指定该共享内存区域在它的虚拟空间中的位置,则由系统在进程的虚拟地址空间中为其找一块区域(从1G开始);否则,就用shmaddr作为映射的虚拟地址。

(If  shmaddr  is NULL, the system chooses a suitable (unused) address a他 which to attach the segment)

3) 检查虚拟地址的合法性(不能超过进程的最大虚拟空间大小—3G,不能太接近堆栈栈顶)。

4) 认证检查。

5) 申请一块内存用于建立数据结构vm_area_struct,填写该结构。

6) 检查该内存区域,将其加入到进程的mm结构和该共享内存对象的vm_area_struct队列中。

共享内存的粘附只是创建一个vm_area_struct数据结构,并将其加入到相应的队列中,此时并没有创建真正的共享内存页。

当进程第一次访问共享虚拟内存的某页时,因为所有的共享内存页还都没有分配,所以会发生一个page fault异常。当Linux处理这个page fault的时候,它找到发生异常的虚拟地址所在的vm_area_struct数据结构。在该数据结构中包含有这类共享虚拟内存的一组处理程序,其中的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值