共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。使用共享存储时要掌握的唯一窍门是,在多个进程之间同步访问一个给定的存储区。若服务器进程正在将数据放入共享存储区,则在它做完这一操作之前,客户进程不应当去取这些数据。通常,信号量用于同步共享存储访问,也可以用记录锁和互斥量。
System V 共享内存区
调用的第一个函数通常是shmget,它获得一个共享存储标识符
#include<sys/shm.h>
int shmget(key_t key,size_t size,int flag);
返回值:若成功,返回共享存储ID,若失败,返回-1;
参数size是该共享存储段的长度,以字节为单位。实现通常将其向上取为系统页长的整数倍。若应用指定的size值并非系统页长的整数倍,那么最后一页的余下部分是不可使用的。如果正在创建一个新段,那么必须指定其size。如果正在引用一个现存的段,则将其size指定 为0。当创建一个新段时,段内的内容初始化为0。
shmctl函数对共享存储段执行多种操作
#incldue<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds* buf);
返回值:若成功,返回0;若出错,返回-1;
IPC_STAT 取此段的shmid_ds结构,并将它存放在buf指向的结构中
IPC_SET 将字段shm_perm.uid、shm_perm.gid、shm_perm.mode 从buf指向的结构复制到与这个共享存储段相关的shmid_ds结构中。
IPC_RMID 从系统中删除该共享存储段。
Linux和Solaris提供了另外两种命令
SHM_LOCK 在内存中对共享存储段加锁,此命令只能有超级用户执行
SHM_UNLOCK 解锁共享存储段,此命令只能由超级用户执行
一旦创建了一个共享存储段,进程就可以调用shmat将其连接到它的地址空间。
#include<sys/shm.h>
void* shmat(int shmid,const void* addr,int flag);
返回值:若成功,返回指向共享存储段的指针;若出错,返回-1;
共享存储段连接到调用进程的哪个地址上与addr参数以及flag中是否指定SHM_RND位有关。
如果addr为0,则此段连接到由内核选择的第一个可用地址上。这是推荐的使用方法。
如果addr非0,并且没有指定SHM_RND,则此段连接到addr指定的地址上。
如果 addr 非0, 并且指定了SHM_RND, 则此段连接到 (addr-(addr mod SHMLBA)) 所表示的地址上。SHM_RND 命令的意思是 “取整’. SHMLBA的意思是 “低边界地址倍数”,它总是2的乘方。 该算式是将地址向下取最近1个SHMLBA的倍数.
除非只在一种硬件上运行应用程序(这在当今是不大可能的),否则不应指定共享存储段所连接到的地址。而是应当指定addr为0,以便由系统选择地址。
如果在flag中指定SHM_RDONLY,则以只读方式连接此段,否则以读写方式连接此段。
当对共享存储段的操作已经结束,则调用shmdt与该段分离。注意,这并不从系统中删除其标识符以及其相关的数据结构。该标识符仍然存在,直到某个进程带IPC_RMID命令的调用shmctl特地删除它为止。
#include<sys/shm.h>
int shmdt(const void* addr);
返回值:若成功,返回0;若出错,返回-1;
下面是运用共享存储实现进程间通信的例子
//shmwrite.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<errno.h>
#define MAXLEN 1024
int main()
{
int shmid;
char buf[MAXLEN];
int n;
void* shmaddr;
int count=0;
if((shmid=shmget((key_t)1234,MAXLEN,0666|IPC_CREAT))<0) //创建共享存储区
{
fprintf(stderr,"shmget error:[%s]\n",strerror(errno));
exit(1);
}
if((shmaddr=shmat(shmid,0,0))<0) //将共享存储区连接到地址空间
{
fprintf(stderr,"shmat error:[%s]\n",strerror(errno));
exit(1);
}
while(fgets(buf,MAXLEN,stdin)!=NULL)
{
memcpy(shmaddr,buf,MAXLEN); //写入到共享存储区
}
shmctl(shmid,IPC_RMID,0);
exit(0);
}
//shmread.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<errno.h>
#define MAXLEN 1024
int main()
{
int shmid;
char buf[MAXLEN];
int n;
void* shmaddr;
if((shmid=shmget((key_t)1234,0,0))<0) //引用已创建的共享存储区
{
fprintf(stderr,"shmget error:[%s]\n",strerror(errno));
exit(1);
}
if((shmaddr=shmat(shmid,0,SHM_RDONLY))<0) //将共享存储区连接到内存空间
{
fprintf(stderr,"shmat error:[%s]\n",strerror(errno));
exit(1);
}
while(1)
{
sleep(5);
memcpy(buf,shmaddr,MAXLEN); //从共享存储区读取数据
printf("%s",buf);
}
shmdt(shmaddr);
exit(0);
}