Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2422631
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: LINUX

2011-04-05 00:38:05

一、什么是共享内存区

共享内存区是最快的可用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。当创建一新段时,段内的内容初始化为0shmflg由九个权限标志构成,它们的用法和创建文件时使用的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 \n”);

    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_id262147

       System V共享内存区至少具有随内核持续性,因此程序结束该共享内存区还存在。

 

在共享内存段刚被创建的时候,任何进程还都不能访问它。为了建立这个共享内存段的访问渠道,必须由我们来把它连接到某个进程的地址空间。这项工作是由shmat函数完成的。

2

名称:

shmat

功能:

将共享内存段连接到他的地址空间

头文件:

#include

#inlcude

函数原形:

void *shmat(int shm_id,void *shm_addr,int shmflg);

参数:

shm_id 标识码

shm_addr 连接到的地址

shmflg  标志位

返回值:

若成功则为指向共享存储的指针,若出错则为-1

      

 

 

 

 

 

 

 

 

 

 

 shm_idshmget返回的共享内存标识码。

       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.cuidshm_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 \n”);

    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_id262147

 

 

当建立共享内存区并把连接到该共享内存区,我就可以通过往该区域写或读来进程进程间通信。我们可以把该区域当成用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 \n”);

    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 \n”);

    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

阅读(1351) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~