1、概述
系统调用mmap通过映射一个普通文件实现共享内存。System V 则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件。执行过程是先调用shmget,再调用shmat。对于每个共享的内存区,内核维护如下的信息结构,
定义在<sys/shm.h>头文件中。
/* One shmid data structure for each shared memory segment in the system. */
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
/* the following are private */
unsigned short shm_npages; /* size of segment (pages) */
unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
struct vm_area_struct *attaches; /* descriptors for attaches */
};
参考网址:http://www.tldp.org/LDP/lpg/node68.html
2、System V 共享内存区API
使用共享内存的流程:
1.进程必须首先分配它。 //shmget
2.随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。//shmat
3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。//shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
/*创建一个新的内存共享区或者访问一个已经存在的共享内存区返回共享内存区标识符: 创建&&打开 */
int shmget(key_t key, size_t size, int shmflg);
ftok(const char* path,int id) //用path创建系统IPC键值
/*创建或打开一个共享内存区后,调用shmat把它连接到调用进程的地址空间*/
void *shmat(int shmid, const void *shmaddr,int shmflg);
/*当一个进程完成某个共享内存区的使用时,调用shmdt断开这个内存区*/
int shmdt(const void *shmaddr);
/*对内存区进行多种操作cmd取值:
IPC_RMID:从系统中删除由shmid标识的共享内存区并拆除它
IPC_SET:给指定的共享内存区设置其shmid_ds结果成员
IPC_STAT:通过buff参数向调用者返回所指定共享内存区当前的shmid_ds结构*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
调用System V API编写程序进行测试:
程序1:调用shmget函数使用指定的路径名和长度创建一个共享内存区,删除共享内存区:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <fcntl.h>
#define SVSHM_MODE (SHM_R|SHM_W|SHM_R >> 3| SHM_R >>6)
int main(int argc,char** argv)
{
int fd,flags;
char* ptr;
int c;
size_t length;
flags = SVSHM_MODE | IPC_CREAT;
//创建指令
while((c = getopt(argc,argv,"e")) != -1)
{
switch(c)
{
case 'e':
flags |= O_EXCL;
break;
}
}
if(optind != argc-2 )
{
perror("usage: shmget [-e] <path> <length>");
exit(0);
}
length = atoi(argv[optind + 1]);
//ftok() 把指定路径转换成系统IPC键值(key);
//以指定键值建立共享内存
fd = shmget(ftok(argv[optind],0),length,flags);
//把内存地址映射到当前进程
ptr = shmat(fd, NULL,0);
/*
to-do with shm
*/
//删除共享内存地址
shmctl(fd,IPC_RMID,NULL);
return 0;
}
对共享内存区读或写:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <fcntl.h>
#define SVSHM_MODE (SHM_R|SHM_W|SHM_R>>3|SHM_R>>6)
int main(int argc,char** argv)
{
int fd;
int i,c;
pid_t child;
unsigned char* ptr;
struct shmid_ds buff;
if(argc != 2)
{
perror("usage: shmset <path>.\n");
exit(1);
}
//打开共享内存,该共享内存区已经存在
fd = shmget(ftok(argv[1],0),0,SVSHM_MODE);
ptr = shmat(fd,NULL,0);
//获取已经存在共享内存区大小
shmctl(fd, IPC_STAT, &buff);
//写共享内存
//if(fork() == 0)
// {
printf("in child:\n");
for(i=0; i<buff.shm_segsz; i++)
*ptr++ = i % 256;
// exit(0);
//}
//wait(NULL);
//printf("in parent:\n");
//读共享内存
//for(i=0;i<buff.shm_segsz;i++)
//{
// c=*ptr++;
// printf("ptr[%d]=%d\n",i,c);
//}
return 0;
}
3、System V 与Posix 共享内存区
二者的差别是:
(1)Posix共享内存区是先调用shm_open然后再调用mmap,System V 共享内存区是先调用shmget再调用shmat。
(2)Posix共享内存区对象的大小可在任何时刻通过ftruncate修改,而System V 共享内存区对象的大小是在调用shmget创建时固定下来的。 //length = 0 , 打开已经建立共享内存区