1.Posix共享:
Posix提供了两种在无亲缘关系进程间共享内存区的方法:
(1)内存映射文件:先有open函数打开,然后调用mmap函数把得到的描述符映射到当前进程地址空间中的一个文件。
(2)共享内存区对象:先有shm_open打开一个Posix IPC名字(也可以是文件系统中的一个路径名),然后调用mmap将返回的描述符映射到当前进程的地址空间。
两种方法多需要调用mmap,差别在于作为mmap的参数之一的描述符的获取手段。
Posix共享内存区对象
Posix共享内存区涉及以下两个步骤要求:
(1)指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或打开一个已经存在的共享内存区对象。
(2)调用mmap把这个共享内存区映射到调用进程的地址空间。
采用Posix共享内存实现给一个共享的计数器持续加1,它由多个进程给存放在共享内存区中的某个计数器持续加1。将计数器放在一个共享内存区中,并用一个有名信号量来同步。
下面看一个例子:
创建并初始化共享内存区和信号量的程序如下:server1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <semaphore.h>
#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
//计数器结构体
struct shmstruct
{
int count;
};
//同步有名信号量
sem_t *mutex;
int main(int argc,char *argv[])
{
int fd;
struct shmstruct *ptr;
if(argc != 3)
{
printf("usage: server1 <shmname> <semname>.\n");
exit(0);
}
//防止所需共享内存区对象已经存在
shm_unlink(argv[1]);
//创建一个新的共享内存区对象
if((fd = shm_open(argv[1],O_RDWR | O_CREAT | O_EXCL,FILE_MODE)) == -1)
{
perror("shm_open error");
exit(-1);
}
//指定新创建的共享内存区对象的大小
ftruncate(fd,sizeof( struct shmstruct));
//将新创建的共享内存区映射到调用进程的地址空间
if((ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
{
perror("mmap error");
exit(-1);
}
//关闭对象描述符
close(fd);
//防止所需的信号量已经存在
sem_unlink(argv[2]);
//创建有名信号量,作为互斥锁用
if((mutex = sem_open(argv[2],O_CREAT|O_EXCL,FILE_MODE,1)) == SEM_FAILED)
{
perror("sem_open error");
exit(-1);
}
//关闭信号量
sem_close(mutex);
exit(0);
}
给存放在共享内存区中的一个计数器加1程序如下:client1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <semaphore.h>
#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
struct shmstruct
{
int count;
};
sem_t *mutex;
int main(int argc,char *argv[])
{
int fd,i,nloop;
pid_t pid;
struct shmstruct *ptr;
if(argc != 4)
{
printf("usage: client1 <shmname> <semname> <#loops>.\n");
exit(0);
}
nloop = atoi(argv[3]);
//打开共享内存区
if((fd = shm_open(argv[1],O_RDWR,FILE_MODE)) == -1)
{
perror("shm_open error");
exit(0);
}
//将共享内存区映射到进程地址空间
if((ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
{
perror("mmap error");
exit(-1);
}
close(fd);
//打开信号量
if((mutex = sem_open(argv[2],0)) == SEM_FAILED)
{
printf("sem_open error");
exit(-1);
}
pid = getpid();
for(i=0;i<nloop;i++)
{
sem_wait(mutex); //锁住信号量
printf("pid %ld: %d\n",(long) pid,ptr->count++);
sem_post(mutex); //释放信号量
}
exit(0);
}
2.system V 共享内存:
System V 共享内存区API
使用共享内存的流程:
1.进程必须首先分配它。
2.随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。
3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。
System V 与Posix 共享内存区
二者的差别是:
(1)Posix共享内存区是先调用shm_open然后再调用mmap,System V 共享内存区是先调用shmget再调用shmat。
(2)Posix共享内存区对象的大小可在任何时刻通过ftruncate修改,而System V 共享内存区对象的大小是在调用shmget创建时固定下来的。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <fcntl.h>
#define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
int main(int argc,char *argv[])
{
int c,id,oflag;
char *ptr;
size_t length;
oflag = SVSHM_MODE | IPC_CREAT;
while(( c = getopt(argc,argv,"e")) != -1)
{
switch(c)
{
case 'e':
oflag |= O_EXCL;
break;
}
}
if (optind != argc -2)
{
printf("usage: shmget [-e] <pathname> <length>.\n");
exit(0);
}
length = atoi(argv[optind + 1]);
//创建由用户指定其名字和大小的共享内存区
id = shmget(ftok(argv[optind],0),length,oflag);
//把该内存区连接到当前进程的地址空间
ptr = shmat(id,NULL,0);
exit(0);
}