进程间通信-共享内存

来源:互联网 发布:云计算应用介绍 编辑:程序博客网 时间:2024/06/03 19:16

共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种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 1024int 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 1024int 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);}
原创粉丝点击