欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

XSI IPC 通信之共享存储

程序员文章站 2022-07-01 09:14:25
...
    在XSI IPC通信之消息队列XSI IPC通信之信号量两节中,我们讨论了消息队列和信号量,这一节将继续讨论同属于 XSI IPC 的存储共享。
    共享存储允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是一种较快的 IPC。要注意的是,当一个进程在修改共享存储区时,其他进程不应该去操作这块区域。通常使用信号量来同步共享存储访问(当然也可使用记录锁或互斥量)。
    mmap 就是共享存储的一种形式,它是将同一个文件映射到各个进程的地址空间。XSI 共享存储和内存映射的文件的区别是,前者没有相关的文件。XSI 共享存储段是内存的匿名段。
    下表给出了影响共享存储的系统限制。
XSI IPC 通信之共享存储
            
    
    博客分类: unix编程 进程间通信XSI IPC存储共享 
    内核为每个共享存储段维护着一个结构,它至少包含下面这些成员。
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

    下图显示了这种存储区布局示意图。
XSI IPC 通信之共享存储
            
    
    博客分类: unix编程 进程间通信XSI IPC存储共享 
  • XSI IPC 通信之共享存储
            
    
    博客分类: unix编程 进程间通信XSI IPC存储共享 
  • 大小: 7.5 KB
  • XSI IPC 通信之共享存储
            
    
    博客分类: unix编程 进程间通信XSI IPC存储共享 
  • 大小: 8.9 KB