共享记忆体

共享记忆体

共享记忆体指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量记忆体。由于多个CPU需要快速访问存储器,这样就要对存储器进行快取(Cache)。任何一个快取的数据被更新后,由于其他处理器也可能要存取,共享记忆体就需要立即更新,否则不同的处理器可能用到不同的数据。共享记忆体是 Unix下的多进程之间的通信方法 ,这种方法通常用于一个程式的多进程间通信,实际上多个程式间也可以通过共享记忆体来传递信息。

    • 中文名:共享记忆体
    • 外文名:shared memory
    • 内容:对存储器进行快取
    • 背景:多个CPU需要快速访问存储器

初始化

shmid_ds结构数据

初 值

shmid_ds结构数据

初 值

shm_lpid

0

shm_dtime

0

shm_nattach

0

shm_ctime

系统当前值

shm_atime

0

shm_segsz

参数 size

下面实例演示了使用shmget函式创建一块共享记忆体。程式中在调用shmget函式时指定key参数值为IPC_PRIVATE,这个参数的意义是创建一个新的共享记忆体区,创建成功后使用shell命令ipcs来显示系统下共享记忆体的状态。命令参数-m为只显示共享记忆体的状态。

(1)在vi编辑器中编辑该程式如下:

程式清单14-8 create_shm.c 使用shmget函式创建共享记忆体

#include#include#include#include#include#defineBUFSZ4096intmain(void){intshm_id;/*共享记忆体标识符*/shm_id=shmget(IPC_PRIVATE,BUFSZ,0666);//shm_id=shmget(999,BUFSZ,0666|O_CREAT);if(shm_id<0){/*创建共享记忆体*/perror(shmget);exit(1);}printf(successfullycreatedsegment:%d\n,shm_id);system(ipcs-m);/*调用ipcs命令查看IPC*/exit(0);}

(2)在shell中编译该程式如下:

$gcc create_shm.c -o create_shm

(3)在shell中运行该程式如下:

$./ create_shm

successfully created segment : 2752516

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x0000000 66root 600 393216 2 dest

0x00000 5209root 666 4096 0

0x0056a4d5 2686978 root 600 488 1

0x0056a4d6 2719747 root 600 131072 1

root 666 4096 0

上述程式中使用shmget函式来创建一段共享记忆体,并在结束前调用了系统shell命令ipcs –m来查看当前系统IPC状态。

特点

所谓共享记忆体就是使得多个进程可以访问同一块记忆体空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。其他进程能把同一段共享记忆体段“连线到”他们自己的地址空间里去。所有进程都能访问共享记忆体中的地址。如果一个进程向这段共享记忆体写了数据,所做的改动会即时被有访问同一段共享记忆体的其他进程看到。共享记忆体的使用大大降低了在大规模数据处理过程中记忆体的消耗,但是共享记忆体的使用中有很多的陷阱,一不注意就很容易导致程式崩溃。

注意事项

共享记忆体相比其他几种方式有着更方便的数据控制能力,数据在读写过程中会更透明。当成功导入一块共享记忆体后,它只是相当于一个字元串指针来指向一块记忆体,在当前进程下用户可以随意的访问。缺点是,数据写入进程或数据读出进程中,需要附加的数据结构控制。

如何创建

共享记忆体是存在于核心级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在档案系统/proc目录下有对其描述的相应档案。函式shmget可以创建或打开一块共享记忆体区。函式原型如下:

#include

int shmget( key_t key, size_t size, int flag );

函式中参数key用来变换成一个标识符,而且每一个IPC对象与一个key相对应。当新建一个共享记忆体段时,size参数为要请求的记忆体长度(以位元组为单位)。

注意:核心是以页为单位分配记忆体,当size参数的值不是系统记忆体页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分记忆体是不可用的。

当打开一个记忆体段时,参数size的值为0。参数flag中的相应许可权位初始化ipc_perm结构体中的mode域。同时参数flag是函式行为参数,它指定一些当函式遇到阻塞或其他情况时应做出的反应。shmid_ds结构初始化如表14-4所示。

参数详解

cmd的值意 义

IPC_STAT:取shm_id所指向记忆体共享段的shmid_ds结构,对参数buf指向的结构赋值

IPC_SET:使用buf指向的结构对sh_mid段的相关结构赋值,只对以下几个域有作用,shm_perm.

uid shm_perm.gid以及shm_perm.mode,注意此命令只有具备以下条件的进程才可以请求:

1.进程的用户ID等于shm_perm.cuid或者等于shm_perm.uid

2.超级用户特权进程

IPC_RMID:删除shm_id所指向的共享记忆体段,只有当shmid_ds结构的shm_nattch域为零时,才会真正执行删除命令,否则不会删除该段,注意此命令的请求规则与IPC_SET命令相同

SHM_LOCK:锁定共享记忆体段在记忆体,此命令只能由超级用户请求

SHM_UNLOCK:对共享记忆体段解锁,此命令只能由超级用户请求

使用函式shmat将一个存在的共享记忆体段连线到本进程空间,其函式原型如下:

#includevoid*shmat(intshm_id,constvoid*addr,intflag);

函式中参数shm_id指定要引入的共享记忆体,参数addr与flag组合说明要引入的地址值,通常只有2种用法,addr为0,表明让核心来决定第1个可以引入的位置。addr非零,并且flag中指定SHM_RND,则此段引入到addr所指向的位置(此操作不推荐使用,因为不会只对一种硬体上运行应用程式,为了程式的通用性推荐使用第1种方法),在flag参数中可以指定要引入的方式(读写方式指定)。

%说明:函式成功执行返回值为实际引入的地址,失败返回–1。shmat函式成功执行会将shm_id段的shmid_ds结构的shm_nattch计数器的值加1。

当对共享记忆体段操作结束时,应调用shmdt函式,作用是将指定的共享记忆体段从当前进程空间中脱离出去。函式原型如下:

#includeintshmdt(void*addr);

参数addr是调用shmat函式的返回值,函式执行成功返回0,并将该共享记忆体的shmid_ds结构的shm_nattch计数器减1,失败返回–1。

下面实例演示了操作共享记忆体段的流程。程式的开始部分先检测用户是否有输入,如出错则列印该命令的使用帮助。接下来从命令行读取将要引入的共享记忆体ID,使用shmat函式引入该共享记忆体,并在分离该记忆体之前睡眠3秒以方便查看系统IPC状态。

(1)在vi编辑器中编辑该程式如下:

程式清单14-9 opr_shm.c 操作共享记忆体段

#include#include#include#include#includeintmain(intargc,char*argv[]){intshm_id;char*shm_buf;if(argc!=2){/*命令行参数错误*/printf(USAGE:atshm);/*列印帮助讯息*/exit(1);}shm_id=atoi(argv[1]);/*得到要引入的共享记忆体段*//*引入共享记忆体段,由核心选择要引入的位置*/if((shm_buf=shmat(shm_id,0,0))<(char*)0){perror(shmat);exit(1);}printf(segmentattachedat%p\n,shm_buf);/*输出导入的位置*/system(ipcs-m);sleep(3);/*休眠*/if((shmdt(shm_buf))<0){/*与导入的共享记忆体段分离*/perror(shmdt);exit(1);}printf(segmentdetached\n);system(ipcs-m);/*再次查看系统IPC状态*/exit(0);}

(2)在shell中编译该程式如下:

$gcc opr_shm.c -o opr_shm

(3)在shell中运行该程式如下:

$./ opr_shm 2752516

segment attached at 0xb7f29000

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x00 0root 600 393216 2 dest

0x00000root 666 4096 0

0x0056a4d5 2686978 root 600 488 1

0x0056a4d6 2719747 root 600 131072 1

0x00000root 666 4096 1

segment detached

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x000 00 65root 600 393216 2 dest

0x000209 root 666 4096 0

0x0056a4d5 2 686978 root 600 488 1

0x0056a4d6 27 19747 root 600 131072 1

0x000002516 root 666 4096 0

上述程式中从命令行中读取所要引入的共享记忆体ID,并使用shmat函式引入该记忆体到当前的进程空间中。注意在使用shmat函式时,将参数addr的值设为0,所表达的意义是由核心来决定该共享记忆体在当前进程中的位置。由于在编程的过程中,很少会针对某一个特定的硬体或系统编程,所以由核心决定引入位置也就是shmat推荐的使用方式。在导入后使用shell命令ipcs –m来显示当前的系统IPC的状态,可以看出输出信息中nattch栏位为该共享记忆体时的引用值,最后使用shmdt函式分离该共享记忆体并列印系统IPC的状态。

结构示意

%说明:图中两个进程同时遵循一定的规则来读写该记忆体。同时,在多进程同步或互斥上也需要附加的代码来辅助共享记忆体机制。

在共享记忆体段中都是以字元串的默认结束符为一条信息的结尾。每个进程在读写时都遵守这个规则,就不会破坏数据的完整性。

操作函式

由于共享记忆体这一特殊的资源类型,使它不同于普通的档案,因此,系统需要为其提供专有的操作函式,而这无疑增加了程式设计师开发的难度(需要记忆额外的专有函式)。使用函式shmctl可以对共享记忆体段进行多种操作,其函式原型如下:

#include

int shmctl( int shm_id, int cmd, struct shmid_ds *buf );

函式中参数shm_id为所要操作的共享记忆体段的标识符,struct shmid_ds型指针参数buf的作用与参数cmd的值相关,参数cmd指明了所要进行的操作,其解释如表14-5所示。

相关词条

相关搜索

其它词条