1.它允许两个不相关的的进程访问同一个逻辑内存。(内存是共享的)
2.它并未提供同步机制,所以我们要通过其他同步手段达到内存访问的同步效果。(一般通过传递小消息来达到同步。)
3.共享内存的函数类似于信号量函数(IPC机制的函数都具有相似之处)
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg); #include <sys/shm.h>
4.创建共享内存:
shmget: key,size字节为单位的指定共享内存大小,IPC_CREAT和一个权限标志位按|运算创建一个新共享内存段。权限标志位就给定了内存读写权限;成功返回非负整数,失败返回-1
shmat:创建好后,并不能被进程共享,我们必须让它映射到我们本进程的进程内存空间才行。这个方法就是这个作用鸟。
*shm_addr:通常给空指针,表示系统自己选定一个进程地址来作为共享内存的映射地址。
shmflg:通常我们给0,还有2种给法:shm_rnd(和shm_addr相关) and shm_rdonly只读:如果创建的访问权限是可写的,但是这里又设置了是rdonly,那么也是rdonly的。
成功返回首地址,失败返回-1.
shmdt:解映射:作用是将共享内存从当前进程中分离,使得该进程无法使用内存共享。成功返回0,失败返回-1.
shmctl:command:IPC_STAT(好像和IPC_SET一样的作用耶) IPC_SET设置值 IPC_RMID删除(这个时候struct buf给0即可)
*buf :指向包含共享内存模式和访问权限的结构。
成功0,失败-1.s
注意:如果shmctl删除一个正处于链接状态的共享内存时,通常这个共享内存段还能够继续使用,直到它从最后一个进程中分离为止。(不过这个并未标准化,所以不要太依赖这个概念)
实际编程中,我们通常使用信号量,传递消息(管道or ipc消息队列),生成信号等方法来达到对共享内存的同步访问。
以下是转的(补充):
1. 数据结构
与消息队列和信号量集合类似,内核为每一个共享内存段(存在于它的地址空间)维护着一个特殊的数据结构shmid_ds,这个结构在include/linux/shm.h中定义如下:
/* 在系统中 每一个共享内存段都有一个shmid_ds数据结构. */
struct shmid_ds {
struct ipc_perm shm_perm; /* 操作权限 */
int shm_segsz; /* 段的大小(以字节为单位) */
time_t shm_atime; /* 最后一个进程附加到该段的时间 */
time_t shm_dtime; /* 最后一个进程离开该段的时间 */
time_t shm_ctime; /* 最后一次修改这个结构的时间 */
unsigned short shm_cpid; /*创建该段进程的 pid */
unsigned short shm_lpid; /* 在该段上操作的最后一个进程的pid */
short shm_nattch; /*当前附加到该段的进程的个数 */
/* 下面是私有的 */
unsigned short shm_npages; /*段的大小(以页为单位) */
unsigned long *shm_pages; /* 指向frames -> SHMMAX的指针数组 */
struct vm_area_struct *attaches; /* 对共享段的描述 */
};
------2. 共享内存的处理过程
某个进程第一次访问共享虚拟内存时将产生缺页异常。这时,Linux 找出描述该内存的 vm_area_struct 结构,该结构中包含用来处理这种共享虚拟内存段的处理函数地址。共享内存缺页异常处理代码对shmid_ds 的页表项表进行搜索,以便查看是否存在该共享虚拟内存的页表项。如果没有,系统将分配一个物理页并建立页表项,该页表项加入 shmid_ds 结构的同时也添加到进程的页表中。这就意味着当下一个进程试图访问这页内存时出现缺页异常,共享内存的缺页异常处理代码则把新创建的物理页给这个进程。(因此说,第一个进程对共享内存的存取引起创建新的物理页面,而其它进程对共享内存的存取引起把那个页加添加到它们的地址空间。)
当某个进程不再共享其虚拟内存时,利用系统调用将共享段从自己的虚拟地址区域中移去,并更新进程页表。当最后一个进程释放了共享段之后,系统将释放给共享段所分配的物理页。
当共享的虚拟内存没有被锁定到物理内存时,共享内存也可能会被交换到交换区中。
--------
系统调用: shmctl()
原型: int shmctl ( int shmqid, int cmd, struct shmid_ds *buf );
返回:成功为 0 , 失败 为-1
这个特殊的调用和semctl()调用几乎相同,因此,这里不进行详细的讨论。有效命令的值是:
IPC_STAT :检索一个共享段的shmid_ds结构,把它存到buf参数的地址中。
IPC_SET :对一个共享段来说,从buf 参数中取值设置shmid_ds结构的ipc_perm域的值。
IPC_RMID :把一个段标记为删除
IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的 shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段。
之所以内存共享是最快速的IPC机制,是因为读它的访问是不需要涉及内核的。所以快。但因此我们也要提供对他的同步机制。