码迷,mamicode.com
首页 > 系统相关 > 详细

XSI进程间通信-----共享内存

时间:2015-07-29 14:12:12      阅读:317      评论:0      收藏:0      [点我收藏+]

标签:进程间通信

1. 基本特点

   1)两个或者更多进程,共享同一块由系统内核负责维护的内存区域,其地址空间通常被映射到堆和栈之间。

     技术分享

       如图所示,每个进程都有自己的share memory,共享内存其实是内核中的一块空间,是对不同进程share memory的映射,对share memory的操作实质就是对内核中共享内存的操作,操作形式和普通内存没什么区别。

   2) 无需复制信息,最快的一种IPC机制,所以适合大文件的传输。
   3) 需要考虑同步访问的问题,不能那边还没写完,这边就读。
   4) 内核为每个共享内存,维护一个shmid_ds结构体形式(描述大小,位置等信息)的共享内存对象

struct shmid_ds {
    struct ipc_perm shm_perm;   // 所有者及其权限
    size_t          shm_segsz;  // 大小(以字节为单位)
    time_t          shm_atime;  // 最后加载时间
    time_t          shm_dtime;  // 最后卸载时间
    time_t          shm_ctime;  // 最后改变时间
    pid_t           shm_cpid;   // 创建进程PID
    pid_t           shm_lpid;   // 最后加载/卸载进程PID
    shmatt_t        shm_nattch; // 当前加载计数
    ...
};

struct ipc_perm {
    key_t          __key; // 键值
    uid_t          uid;   // 有效属主ID
    gid_t          gid;   // 有效属组ID
    uid_t          cuid;  // 有效创建者ID
    gid_t          cgid;  // 有效创建组ID
    unsigned short mode;  // 权限字
    unsigned short __seq; // 序列号
};


2. 常用函数:

1) 创建/获取共享内存

int shmget (key_t key, size_t size, int shmflg);
A.  该函数以key参数为键值创建共享内存, 或获取已有的共享内存。
B.  size参数为共享内存的字节数, 建议取内存页字节数(4096)的整数倍。若希望创建共享内存,则必需指定size参数。若只为获取       已有的共享内存,则size参数可取0。      
C. shmflg取值:    
     0        - 获取,不存在即失败。
     PC_CREAT - 创建,不存在即创建,已存在即获取,除非...或了一个IPC_EXCL
     IPC_EXCL - 排斥,已存在即失败

D. 成功返回共享内存标识,失败返回-1。

2) 加载共享内存 , 即建立一个映射
void* shmat (int shmid, const void* shmaddr,int shmflg);
A. 将shmid参数所标识的共享内存,映射到调用进程的地址空间。
B. 可通过shmaddr参数人为指定映射地址,也可将该参数置NULL,由系统自动选择。
C. shmflg取值:
          0          - 以读写方式使用共享内存。
     SHM_RDONLY - 以只读方式使用共享内存。
     SHM_RND    - 只在shmaddr参数非NULL时起作用。表示对该参数向下取内存页的整数倍,作为映射地址。

D. 成功返回映射地址,失败返回-1。
E. 内核将该共享内存的加载计数加1。(映射是有计数的,类似于单例)
3) 卸载共享内存  即 解除映射
int shmdt (const void* shmaddr);
A. 从调用进程的地址空间中,取消由shmaddr参数所指向的,共享内存映射区域。
B. 成功返回0,失败返回-1。
C. 内核将该共享内存的加载计数减1。

4) 销毁/控制共享内存

int shmctl (int shmid, int cmd, struct shmid_ds* buf);
A. cmd取值:
     IPC_STAT - 获取共享内存的属性,通过buf参数输出。
     IPC_SET  - 设置共享内存的属性,通过buf参数输入,仅以下三个属性可设置:
           shmid_ds::shm_perm.uid
           shmid_ds::shm_perm.gid
           shmid_ds::shm_perm.mode

     IPC_RMID - 标记删除共享内存。并非真正删除共享内存,只是做一个删除标记, 禁止其被继续加载,但已有加载依然保留只有当 该共享内存的加载计数为0时,才真正被删除。
B. 成功返回0,失败返回-1。


3.编程模型

技术分享

wshm.c

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

int main (void) {
	printf ("创建共享内存...\n");
	key_t key = ftok (".", 100);
	if (key == -1) {
		perror ("ftok");
		return -1;
	}

	int shmid = shmget (key, 4096, 0644 | IPC_CREAT | IPC_EXCL);
	if (shmid == -1) {
		perror ("shmget");
		return -1;
	}

	printf ("加载共享内存...\n");
	void* shmaddr = shmat (shmid, NULL, 0);
	if (shmaddr == (void*)-1) {
		perror ("shmat");
		return -1;
	}

	printf ("写入共享内存...\n");
	sprintf (shmaddr, "我是%u进程写入的数据。", getpid ());
	printf ("按<回车>卸载共享内存(0x%08x/%d)...", key, shmid);
	getchar ();

	if (shmdt (shmaddr) == -1) {
		perror ("shmdt");
		return -1;
	}

	printf ("按<回车>销毁共享内存(0x%08x/%d)...", key, shmid);
	getchar ();

	if (shmctl (shmid, IPC_RMID, NULL) == -1) {
		perror ("shmctl");
		return -1;
	}
	printf ("大功告成!\n");
	return 0;
}
技术分享

使用ipcs -m  命令我们可以得到系统中刚创建的共享内存:

技术分享

rshm.c

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

int shmstat (int shmid) {
	struct shmid_ds shm;
	if (shmctl (shmid, IPC_STAT, &shm) == -1) {
		perror ("shmctl");
		return -1;
	}

	printf ("------------------------------------------------\n");
	printf ("                  共享内存信息\n");
	printf ("----+----------------+--------------------------\n");
	printf (" 所 | 键值           | 0x%08x\n", shm.shm_perm.__key);
	printf (" 有 | 有效属主ID     | %u\n", shm.shm_perm.uid);
	printf (" 者 | 有效属组ID     | %u\n", shm.shm_perm.gid);
	printf (" 及 | 有效创建者ID   | %u\n", shm.shm_perm.cuid);
	printf (" 其 | 有效创建组ID   | %u\n", shm.shm_perm.cgid);
	printf (" 权 | 权限字         | %#o\n", shm.shm_perm.mode);
	printf (" 限 | 序列号         | %u\n", shm.shm_perm.__seq);
	printf ("----+----------------+--------------------------\n");
	printf (" 大小(字节)          | %u\n", shm.shm_segsz);
	printf (" 最后加载时间        | %s", ctime (&shm.shm_atime));
	printf (" 最后卸载时间        | %s", ctime (&shm.shm_dtime));
	printf (" 最后改变时间        | %s", ctime (&shm.shm_ctime));
	printf (" 创建进程ID          | %u\n", shm.shm_cpid);
	printf (" 最后加载/卸载进程ID | %u\n", shm.shm_lpid);
	printf (" 当前加载计数        | %u\n", shm.shm_nattch);
	printf ("---------------------+--------------------------\n");

	return 0;
}

int shmset (int shmid) {
	struct shmid_ds shm;
	if (shmctl (shmid, IPC_STAT, &shm) == -1) {
		perror ("shmctl");
		return -1;
	}

	shm.shm_perm.mode = 0600;
	shm.shm_segsz = 8192;

	if (shmctl (shmid, IPC_SET, &shm) == -1) {
		perror ("shmctl");
		return -1;
	}

	return 0;
}

int main (void) {
	printf ("获取共享内存...\n");
	key_t key = ftok (".", 100);
	if (key == -1) {
		perror ("ftok");
		return -1;
	}

	int shmid = shmget (key, 0, 0);
	if (shmid == -1) {
		perror ("shmget");
		return -1;
	}

	printf ("加载共享内存...\n");

	void* shmaddr = shmat (shmid, NULL, 0);
	if (shmaddr == (void*)-1) {
		perror ("shmat");
		return -1;
	}

	shmstat (shmid);
	printf ("读取共享内存...\n");
	printf ("共享内存(0x%08x/%d):%s\n", key, shmid, shmaddr);
	printf ("卸载共享内存...\n");
	if (shmdt (shmaddr) == -1) {
		perror ("shmdt");
		return -1;
	}

	shmstat (shmid);
	printf ("设置共享内存...\n");
	shmset (shmid);
	shmstat (shmid);
	printf ("大功告成!\n");
	return 0;
}
技术分享

版权声明:本文为博主原创文章,未经博主允许不得转载。

XSI进程间通信-----共享内存

标签:进程间通信

原文地址:http://blog.csdn.net/meetings/article/details/47124775

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!