XSI IPC 通信之共享存储

在[url=http://aisxyz.iteye.com/admin/blogs/2418648]XSI IPC通信之消息队列[/url]和[url=http://aisxyz.iteye.com/admin/blogs/2418777]XSI IPC通信之信号量[/url]两节中,我们讨论了消息队列和信号量,这一节将继续讨论同属于 XSI IPC 的存储共享。
共享存储允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是一种较快的 IPC。要注意的是,当一个进程在修改共享存储区时,其他进程不应该去操作这块区域。通常使用信号量来同步共享存储访问(当然也可使用记录锁或互斥量)。
mmap 就是共享存储的一种形式,它是将同一个文件映射到各个进程的地址空间。XSI 共享存储和内存映射的文件的区别是,前者没有相关的文件。XSI 共享存储段是内存的匿名段。
下表给出了影响共享存储的系统限制。
[img]http://dl2.iteye.com/upload/attachment/0129/5784/31984289-bd63-30ea-9cf1-a663e337bec3.png[/img]
内核为每个共享存储段维护着一个结构,它至少包含下面这些成员。

struct shmid_ds{
struct ipc_perm shm_perm;
size_t shm_segsz; // size of segment in bytes
pid_t shm_lpid; // pid of last shmop()
pid_t shm_cpid; // pid of creator
shmatt_t shm_nattch; // number of current attaches
time_t shm_atime; // last-attach time
time_t shm_dtime; // last-detach time
time_t shm_ctime; // last-change time
/* ... */
};

其中的 ipc_perm 结构见[url=http://aisxyz.iteye.com/admin/blogs/2410513]XSI IPC 相似特征介绍[/url]。shmatt_t 类型定义为无符号整型,它至少与 unsigned short 一样大。
shmget 函数可以创建或获得一个共享存储标识符。shmctl 函数可对共享存储段执行多种操作。shmat 函数可将创建好的共享存储段连接到进程的地址空间中。shmdt 函数可将共享段与进程分离。

#include <sys/shm.h>
int shmget(key_t key, size_t size, int flag);
/* 返回值:若成功,返回共享存储 ID;否则,返回 -1 */
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/* 返回值:若成功,返回 0;否则,返回 -1 */
void *shmat(int shmid, const void *addr, int flag);
/* 返回值:若成功,返回指向共享存储段的指针;否则,返回 -1 */
int shmdt(const void *addr);
/* 返回值:若成功,返回 0;否则,返回 -1 */

shmget 函数在创建一个新段时,会初始化 shmid_ds 结构的下列成员。
* ipc_perm 结构按[url=http://aisxyz.iteye.com/admin/blogs/2410513]XSI IPC 相似特征介绍[/url]一节中的所述进行初始化。该结构中的 mode 按 flag 中的相应权限位设置。
* shm_lpid、shm_nattach、shm_atime 和 shm_dtime 都设置为 0。
* shm_ctime 设置为当前时间。
* shm_segsz 设置为请求的 size。
参数 size 是共享存储段的长度,以字节为单位,通常将其向上取为系统页长的整数倍。若应用指定的 size 值并非系统页长的整倍数,则最后一页的余下部分是不可使用的。如果是在引用一个现存的段,则可将 size 指定为 0。创建一个新段时,段内的内容会被初始化为 0。
shmctl 函数的 cmd 参数可指定下列 5 种命令中的一种,使其在 shmid 指定的段上执行。
* IPC_STAT:取此段的 shmid_ds 结构,并将其存放在 buf 指向的结构中。
* IPC_SET:将字段 shm_perm.uid、shm_perm.gid、shm_perm.mode 从 buf 指向的结构复制到此队列关联的 shmid_ds 结构中。此命令只能由下列两种进程执行:一种是其有效用户 ID 等于 shm_perm.cuid 或 shm_perm.uid,另一种是具有超级用户特权的进程。
* IPC_RMID:从系统中删除该共享存储段。因为每个共享存储段维护着一个连接计数(及 shm_nattch字段),所以除非使用该段的最后一个进程终止或与该段分离,否则不会实际上删除该存储段。不过不管此段是否仍在使用,该段标识符都会被立即删除,不能再用 shmat 与该段连接。该命令也只能由上面提及的两种进程执行。
Linux 和 Solaris 还提供了如下的另外两种命令。
* SHM_LOCK:在内存中对共享存储段加锁。只能由超级用户执行。
* SHM_UNLOCK:解锁共享存储段。也只能由超级用户执行。
共享存储段连接到调用进程的哪个地址上与 shmat 函数的 addr 参数以及 flag 中是否指定了 SHM_RND 位有关,有以下几种情况。
(1)如果 addr 为 0,则此段连接到由内核选择的第一个可用地址上(推荐)。
(2)如果 addr 非 0,并且没有指定 SHM_RND,则此段连接到 addr 所指定的地址上。
(3)如果 addr 非 0,并且指定了 SHM_RND,则此段连接到(addr - (addr mod SHMLBA))所表示的地址上。SHM_RND 命令的意思是“取整”,SHMLBA 的意思是“低边界地址倍数”,它总是 2 的乘方,该算式是将地址向下取最近 1 个 SHMLBA 的倍数。
如果在 flag 中指定了 SHM_RDONLY 位,则以只读方式连接此段,否则以读写方式连接。
shmat 的返回值是该段所连接的实际地址,如果出错则返回 -1。如果 shmat 成功执行,那么内核将使相应 shmid_ds 结构中的 shm_nattch 计数器值加一。
当对共享存储段的操作已经结束时,就可调用 shmdt 函数与该段分离。不过这并不从系统中删除其标识符以及相关的数据结构,该标识符仍然存在,直至某个进程调用带 IPC_RMID 命令的 shmctl 函数来特地删除它为止。其中的 addr 参数是之前调用 shmat 时的返回值。如果成功,shmdt 将使相关 shmid_ds 结构中的 shm_nattch 计数器减一。
下面这个实例演示了将 addr 参数设为 0 的 shmat 函数的用法,它打印了一些特定系统存放各种类型的数据的位置信息(这些信息的说明见[url=http://aisxyz.iteye.com/admin/blogs/2390606]C 存储及环境[/url]一节)。

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

#define ARRAY_SIZE 40000
#define MALLOC_SIZE 100000
#define SHM_SIZE 100000
#define SHM_MODE 0600 // user read/write

char array[ARRAY_SIZE]; // uninitialized data = bss

int main(void){
int shmid;
char *ptr, *shmptr;

printf("array[] from %p to %p\n", &array[0], &array[ARRAY_SIZE]);
printf("stack around %p\n", &shmid);

if((ptr=malloc(MALLOC_SIZE)) == NULL){
printf("malloc error\n");
exit(1);
}
printf("malloced from %p to %p\n", ptr, ptr+MALLOC_SIZE);

shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE);
if((shmptr=shmat(shmid, 0, 0)) != (void *)-1){
printf("shared memory attached from %p to %p\n", shmptr, shmptr+SHM_SIZE);
shmctl(shmid, IPC_RMID, 0);
}
exit(0);
}

在一个基于 Intel 的 64 位 Linux 系统上运行此程序的结果如下:

$ ./prtMemoryLayout.out
array[] from 0x6020c0 to 0x60bd00
stack around 0x7fff957b146c
malloced from 0x9e3010 to 0x9fb6b0
shared memory attached from 0x7fba578ab000 to 0x7fba578c36a0

下图显示了这种存储区布局示意图。
[img]http://dl2.iteye.com/upload/attachment/0129/6223/c5bcdf5c-90d7-345f-bc56-0b486f96847c.png[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值