分类: LINUX
2013-06-05 14:46:49
原文地址:(7)进程间通信之System V共享内存 作者:g_programming
一、什么是共享内存区
共享内存区是最快的可用IPC形式。它允许多个不相关的进程去访问同一部分逻辑内存。如果需要在两个运行中的进程之间传输数据,共享内存将是一种效率极高的解决方案。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传输就不再涉及内核。这样就可以减少系统调用时间,提高程序效率。
共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中。其他进程可以把同一段共享内存段“连接到”它们自己的地址空间里去。所有进程都可以访问共享内存中的地址。如果一个进程向这段共享内存写了数据,所做的改动会立刻被有访问同一段共享内存的其他进程看到。
要注意的是共享内存本身没有提供任何同步功能。也就是说,在第一个进程结束对共享内存的写操作之前,并没有什么自动功能能够预防第二个进程开始对它进行读操作。共享内存的访问同步问题必须由程序员负责。可选的同步方式有互斥锁、条件变量、读写锁、纪录锁、信号灯。
共享内存区分为System V共享内存区和Posix共享内存区。本节介绍System V共享内存区。
二、共享内存区结构
和其他XSI IPC一样,内核为每个共享存储段设置一个shmid_ds结构。
struct shmid_ds{
struct ipc_perm shm_perm; /*operation perms*/
int shm_segez; /*size of segment*/
time_t shm_atime; /*last attach time*/
time_t shm_dtime; /*last detach time*/
time_t shm_ctime; /*last change time*/
unsigned short shm_lpid; /*pid of creator*/
unsigned short shm_cpid; /*pid of last operator*/
short shm_nattch; /*no.of current attaches*/
…….
};
其中ipc_perm是我们在XSI IPC里介绍的权限结构。
struct ipc_perm{
key_t key;
ushort uid; /*owner euid and egid*/
ushort gid;
ushort cuid; /*creator euid and egid*/
ushort cgid;
ushort mode; /*lower 9 bits of shmflg*/
ushort seq; /*sequence number*/
};
三、共享内存区函数
shmget函数创建一个尚未存在的共享内存区,或者访问一个已存在的共享内存区。
1.
名称:: |
shmget |
功能: |
获得一个共享存储标识符 |
头文件: |
#inlcude #include |
函数原形: |
int shmget(key_t key,int size,int shmflg); |
参数: |
ket 键 size 内存区大小 shmflg 权限值 |
返回值: |
若成功则返回共享内存id,若出错则为-1 |
key为共享存储的外部键,通过ftok获得。参数size是该共享存储段的长度。如果正在创建一个新段,则必须指定其size。如果正在引用一个现存的段,则将size指定为0。当创建一新段时,段内的内容初始化为0。shmflg由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。当需要创建新的共享内存段时需要与IPC_CREAT标志按位或。设置IPC_CREAT标志并传递已存在的共享内存段不会产生错误。如果想创建一个读一无二的共享内存区可以与IPC_CREAT|IPC_EXCL按位或,这样如果系统以存在这个共享内存区,shmget函数就会报错。
下面是一个创建共享内存区的例子;
/*shmget.c创建共享内存区*/ #include #include #include
int main(int argc,char **argv) { int c,id,oflag; char *ptr; size_t length; oflag=0644|IPC_CREAT;
if(argc!=3) { printf(“usage:shmget exit(1); }
length=atoi(argv[2]); id=shmget(ftok(argv[1],0),length,oflag); printf(“shm_id: %d\n”,id); exit(0); } |
运行结果为:
#cc –o shmget shmget.c
#./shmget test 100
shm_id:262147
System V共享内存区至少具有随内核持续性,因此程序结束该共享内存区还存在。
在共享内存段刚被创建的时候,任何进程还都不能访问它。为了建立这个共享内存段的访问渠道,必须由我们来把它连接到某个进程的地址空间。这项工作是由shmat函数完成的。
2、
名称:: |
shmat |
功能: |
将共享内存段连接到他的地址空间 |
头文件: |
#include #inlcude |
函数原形: |
void *shmat(int shm_id,void *shm_addr,int shmflg); |
参数: |
shm_id 标识码 shm_addr 连接到的地址 shmflg 标志位 |
返回值: |
若成功则为指向共享存储的指针,若出错则为-1 |
shm_id是shmget返回的共享内存标识码。
shm_addr是把共享内存连接到当前进程去的时候准备放置它的那个地址。这通常是一个空指针,表示把选择共享内存出现处的地址这项工作交给系统去完成。
shmflg是一组按位或的标志。它的两个可能值是SHM_RND(这个标志与shm_addr一起控制着共享连接的地址)和SHM_RDONLY(它使连接的共享内存成为一个只读区间)。很少有需要控制共享内存连接的地址的情况,一般都是由系统替你挑选一个地址,否则就会使你的软件对硬件的依赖性过高。
shmat的返回值是该段所连接的实际地址,如果出错则返回-1。如果shmat成功执行,那么内核将该共享存储段shmid_ds结构的shm_nattch计算器加1。
缺省情况下,只要调用进程具有某个共享内存区的读写权限,它附接该内存区后就能够同时读写该内存区。只有flag参数指定SHM_RDONLY值时,它以只读方式访问。
当一个进程完成某个共享内存区的使用时,它可调用shmdt函数脱离与这个共享内存区的联系。
3、
名称:: |
shmdt |
功能: |
脱接共享存储段 |
头文件: |
#include #inlcude |
函数原形: |
int shmdt(void *shmaddr); |
参数: |
shmaddr |
返回值: |
若成功则为0,若出错则为-1 |
当一个进程终止时,它的所有当前附接着的共享内存区都自动断接掉。注意本函数调用并不是从系统中删除其标识符以及其数据结构。该标识符仍然存在,直至某个进程调用shmctl特地删除它。
addr参数是以前调用shmat时的返回值。如果成功,shmdt将使相关shmid_ds结构中的shm_nattch计数其减1。
4、
名称:: |
shmctl |
功能: |
对共享存储段执行多种操作 |
头文件: |
#include #inlcude |
函数原形: |
int shmctl(int shm_id,int command,struct shmid_ds *buf); |
参数: |
shm_id 共享内存标识码 command 功能选择 buf 指向shmid_ds结构的指针 |
返回值: |
若成功则为0,若出错则为-1 |
shmctl提供了对一个共享内存区的多种操作。
shmid为共享存储的ID,用于内部标识共享存储。cmd参数指定下列命令中的一种,使其在shmid指定的段上执行。
IPC_STAT 取此段的shmid_ds结构,并将它存放在由buf指向的结果中。
IPC_SET 按buf指向结构中的值设置与此段相关结构中的下列三个字段:shm_perm.uid , shm_perm.gid 和shm_perm.mode.此命令只对有效用户ID等于shm_perm.cuid或shm_perm.uid的进程和具有超级用户特权的用户有效。
IPC_RMID 从系统中删除该共享存储段。因为每个共享存储段有一个连接计数,所以除非使用该段的最后一个进程终止或与该段脱节,否则不会世界上删除该段。
SHM_LOCK 将共享存储段锁定在内存中。此命令只能用超级用户执行。
SGM_UNLOCK 解锁共享存储段。此命令只能用超级用户执行。
buf是一个指针,它指向一个保存着共享内存的模式状态和访问权限的数据结构。
下面是一些程序的例子:
/*shmrmid.c删除共享内存区*/ #include #include #include
int main(int argc,char **argv) { int id; if(argc!=2) { printf(“usage:shmrmid exit(1); } if((id=shmget(ftok(argv[1],0,0))<0) perror(“shmget”); if((shmctl(id,IPC_RMID,NULL))<0) perror(“shmctl”); printf(“shm_id: %d\n”,id); exit(0); } |
运行结果:
#cc –o shmrmid shmrmid.c
#./shmrmid test
shm_id:262147
当建立共享内存区并把连接到该共享内存区,我就可以通过往该区域写或读来进程进程间通信。我们可以把该区域当成用malloc申请的内存一样操作。
/*shmwrite.c向共享内存区写数据*/ #include #include #include
main(int argc,char **argv) { int i,id; unsigned char *ptr; struct shmid_ds buff;
if(argc!=2) { printf(“usage:shmread exit(1); } if((id=shmget(ftok(argv[1],0),0,0))<0) perror(“shmget”); if((ptr=shmat(id,NULL,0))<0) perror(“shmat”); strcpy(ptr,”hello linux”); exit(0); } |
#cc- o shmwrite shmwrite.c
#./shmwrite test
该程序向以前我们建立的共享内存区写入“hello linux”,程序没有输出。我们可以通过下面的程序把数据读出。
/*shmread.h从共享内存区读出数据*/ #include #include #include
main(int argc,char **argv) { int i,id; unsigned char *ptr; struct shmid_ds buff;
if(argc!=2) { printf(“usage:shmread exit(1); } if((id=shmget(ftok(argv[1],0),0,0))<0) perror(“shmget”); ptr=shmat(id,NULL,0); printf(“%s\n”,ptr); exit(0); } |
下面是运行结果:
#cc –o shmread shmread.c
#./shmread test
hello linu