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

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;
}

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

时间: 2024-11-10 00:13:49

XSI进程间通信-----共享内存的相关文章

C# 进程间通信(共享内存)

原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通信,首先要建立一个进程间共享的内存地址,创建好共享内存地址后,一个进程向地址中写入数据,另外的进程从地址中读取数据. 在数据的读写的过程中要进行进程间的同步. 进程间数据同步可以有以下的方式 1. 互斥量Mutex 2. 信号量Semaphore 3. 事件Event 本文中进程间的同步采用 信号量

Linux进程间通信——共享内存

下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒:共

Linux 进程间通信 - 共享内存shmget方式

共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容.这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中.但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址 以下是使用共享内存机制进行进程间通信的基本操作: 需要包含的头文件: #include

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

一.共享内存定义 (百度百科)共享内存指在多处理器的计算机系统中,可以被不同中央处理器访问的大量内存.由于多个CPU需要快速访问存储器,这样就要对存储器进程缓存.任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则,不同的处理器可能用到不同的数据. 在Linux系统中,共享内存允许一个进程或多个进程共享一个给定的存储区(共享内存).不同进程之间共享的内存通常安排为同一段物理地址.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址

Linux进程间通信 -- 共享内存 shmget()、shmat()、shmdt()、shmctl()

下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒

Linux进程间通信—共享内存

五.共享内存(shared memory) 共享内存映射为一段可以被其他进程访问的内存.该共享内存由一个进程所创建,然后其他进程可以挂载到该共享内存中.共享内存是最快的IPC机制,但由于linux本身不能实现对其同步控制,需要用户程序进行并发访问控制,因此它一般结合了其他通信机制实现了进程间的通信,例如信号量. 共享内存与多线程共享global data和heap类似.一个进程可以将自己内存空间中的一部分拿出来,允许其它进程读写.当使用共享内存的时候,我们要注意同步的问题.我们可以使用 sema

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

一.共享内存 共享内存是最高效的通信方式,因为不需要一个进程先拷贝到内核,另一个进程在存内核中读取. 二. ipcs -m 查看共享内存 ipcrm -m 删除共享内存 三.主要函数 shmget 创建 shmctl 删除 shmat 挂接 shmdt 取消挂接 ********* man 函数名 查看***** 四.代码实现 comm.h   1 #pragma once   2 #include<stdio.h>   3 #include<stdlib.h>   4 #incl

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

什么是共享内存? 共享内存就是允许两个不相关的进程访问同一块物理内存.进程可将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址.如果某一个进程向共享内存中写入数据,所做的改动将立即影响到可以访问同一段共享内存的其他进程. 函数接口 (1)创建共享内存 函数原型:int shmget(key_t key,size_t size,int shmflg) key :由ftok()函数返回的标识符 size:以字节为单位的需要共享的内存容量 shmflg: IPC_CREAT

Linux进程间通信 共享内存+信号量+简单例子

每一个进程都有着自己独立的地址空间,比如程序之前申请了一块内存,当调用fork函数之后,父进程和子进程所使用的是不同的内存.因此进程间的通信,不像线程间通信那么简单.但是共享内存编程接口可以让一个进程使用一个公共的内存区段,这样我们便能轻易的实现进程间的通信了(当然对于此内存区段的访问还是要控制好的). 共享内存实现进程通信的优点: 共享内存是进程通信方式中最快速的方式之一,它的快速体现在,为数据共享而进行的复制非常少.这里举例来说,使用消息队列时,一个进程向消息队列写入消息时,这里有一次数据的