- 浏览: 144667 次
-
文章分类
最新评论
在XSI IPC通信之消息队列和XSI IPC通信之信号量两节中,我们讨论了消息队列和信号量,这一节将继续讨论同属于 XSI IPC 的存储共享。
共享存储允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是一种较快的 IPC。要注意的是,当一个进程在修改共享存储区时,其他进程不应该去操作这块区域。通常使用信号量来同步共享存储访问(当然也可使用记录锁或互斥量)。
mmap 就是共享存储的一种形式,它是将同一个文件映射到各个进程的地址空间。XSI 共享存储和内存映射的文件的区别是,前者没有相关的文件。XSI 共享存储段是内存的匿名段。
下表给出了影响共享存储的系统限制。
内核为每个共享存储段维护着一个结构,它至少包含下面这些成员。
其中的 ipc_perm 结构见XSI IPC 相似特征介绍。shmatt_t 类型定义为无符号整型,它至少与 unsigned short 一样大。
shmget 函数可以创建或获得一个共享存储标识符。shmctl 函数可对共享存储段执行多种操作。shmat 函数可将创建好的共享存储段连接到进程的地址空间中。shmdt 函数可将共享段与进程分离。
shmget 函数在创建一个新段时,会初始化 shmid_ds 结构的下列成员。
* ipc_perm 结构按XSI IPC 相似特征介绍一节中的所述进行初始化。该结构中的 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 函数的用法,它打印了一些特定系统存放各种类型的数据的位置信息(这些信息的说明见C 存储及环境一节)。
在一个基于 Intel 的 64 位 Linux 系统上运行此程序的结果如下:
下图显示了这种存储区布局示意图。
共享存储允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是一种较快的 IPC。要注意的是,当一个进程在修改共享存储区时,其他进程不应该去操作这块区域。通常使用信号量来同步共享存储访问(当然也可使用记录锁或互斥量)。
mmap 就是共享存储的一种形式,它是将同一个文件映射到各个进程的地址空间。XSI 共享存储和内存映射的文件的区别是,前者没有相关的文件。XSI 共享存储段是内存的匿名段。
下表给出了影响共享存储的系统限制。

内核为每个共享存储段维护着一个结构,它至少包含下面这些成员。
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 结构见XSI IPC 相似特征介绍。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 结构按XSI IPC 相似特征介绍一节中的所述进行初始化。该结构中的 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 函数的用法,它打印了一些特定系统存放各种类型的数据的位置信息(这些信息的说明见C 存储及环境一节)。
#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
下图显示了这种存储区布局示意图。

发表评论
-
打开伪终端设备
2018-07-09 20:50 1265在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1576伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 812多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 977终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 578尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 957虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 717tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 828终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 452终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 589在XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 629在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 512消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 492有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 437FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 404管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 915readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 470POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 646记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 449UNIX 系统中,守护进程遵循下列通用惯例。 ... -
守护进程编写规则与出错记录
2017-12-26 01:53 464在编写守护进程程 ...
相关推荐
Linux中的XSI IPC(System V Interprocess Communication)是系统V接口的一种实现,它提供了一种在不同进程间共享数据的方法。XSI IPC包括了三种主要机制:消息队列、信号量和共享内存。这些机制允许进程之间进行...
其中,信号量结合共享存储区(ShareMemory)和消息传递统称为XSI IPC(扩展系统接口进程间通信),提供了一种高效的进程间通信机制。 #### 共享存储与信号量:一种高效IPC方案 文章进一步探讨了信号量与共享存储区...
15. 进程间通信:详细讨论了几种UNIX进程间通信的机制,如管道、协同进程、命名管道(FIFO)、XSIIPC(包括消息队列、共享内存和信号量)以及这些机制在C/S模型中的应用比较。 16. 网络IPC:套接字:最后一章介绍了...
415 15.6.2 权限结构 416 15.6.3 结构限制 417 15.6.4 优点和缺点 417 15.7 消息队列 418 15.8 信号量 422 15.9 共享存储 427 15.10 客户进程-服务器进程属性 432 15.11 小结 434 习题 434 第16章...
15.9 共享存储427 15.10 客户进程-服务器进程属性432 15.11 小结434 习题434 第16章网络ipc:套接字437 16.1 引言437 16.2 套接字描述符437 16.3 寻址439 16.3.1 字节序440 16.3.2 地址格式441 16.3.3 ...
15.9 共享存储427 15.10 客户进程-服务器进程属性432 15.11 小结434 习题434 第16章网络ipc:套接字437 16.1 引言437 16.2 套接字描述符437 16.3 寻址439 16.3.1 字节序440 16.3.2 地址格式441 16.3.3 ...