学习环境centos6.5 Linux内核2.6
共享内存允许两个或更多进程访问同一块内存。当一个进程改变了这块内存中的内容的的时候,其他进程都会察觉到这个更改。
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。就像访问进程独有的内存区域一样快,并不需要通过系统调用或其他需要切入内核的过程来完成。同时还能避免对数据的各种不必要的复制。
因为系统内核没有对访问共享内存的进程进行同步机制,如果有这方面的需求,比如需要不同进程进程对共享内存区进行读写操作,则必须要提供自己的同步措施,保护临界资源。通常我们使用信号量机制实现进程间同步与互斥。有关信号量的知识请阅读另一篇博客信号量。
在Linux中,每个进程都有自己的P C B 和地址空间,并且都有一个对应的页表,负责将进程的虚拟内存和物理内存进行映射,通过MMU来管理。共享内存块,被映射到不同进程的地址空间内,从而实现了高效率的资源共享。
分配一个新的共享内存块会创建新的内存页面。因为所有进程都希望共享对同一块内存的访问,只应由一个进程创建一块新的共享内存。再次分配一块已经存在的内存块不会创建新的页面,会返回一个标识该内存块的标示符。一个进程如需使用这个共享内存块,则首先需要将它绑定到自己的地址空间中。会创建一个从进程本身虚拟地址到共享页面的映射关系。当不需要该共享内存块的时候,必须由一个且只能是一个进程负责释放这个被共享的内存页面。
要使用一块共享内存,进程必须先分配它。其他需要访问这个共享内存块的每一个进程都必须将这个共享绑定(attach)到自己的地址空间中(系统维护一个对该内存的引用计数器,通过ipcs -s 命令可查看有几个进程在使用该共享内存块)
。当通信完毕后,所有进程从共享内存块脱离,由一个进程释放该共享内存块。要注意的是,所有用户申请的共享内存块最终大小都必须是向上取整为系统页面大小的整数倍。在Linux系统中,内存页面大小默认是4KB。
注意:当一个进程创建一块共享内存后,该进程在主动去释放该共享内存之前,被kill掉时,只会使该进程脱离(detach)该共享内存块,而不会释放该共享内存块,这时候可以使用命名ipcrm -m 去释放该资源。
#include
#include
int shmget(key_t key, size_t size, int shmflg);
#include
#include
void *shmat(int shmid, const void shmaddr, int shmflg);
#include
#include
void *shmdt(const void* shmaddr);
#include
#Include
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
目的阐述:进程server 创建一块大小为4096kb的共享内存,然后将进程attach到该共享内存块上,并执行写操作,从’a’开始写,每写一次sleep(1);而client进程每隔一秒从该共享内存块读取一个并打印。
#ifndef _COMM_H_
#define _COMM_H_
#include
#include
#include
#include
#define PATHNAME "." // ftok函数 生成key使用
#define PROJ_ID 66 // ftok 函数生成key使用
int create_shm( int size);// 分配指定大小的共享内存块
int destroy_shm( int shmid); // 释放指定id的共享内存块
int get_shmid(); // 获取已经存在的共享内存块
#endif /*_COMM_H_*/
#include "comm.h"
//
static int comm_shm(int size, int shmflag)
{
key_t key = ftok(PATHNAME, PROJ_ID); // 获取key
if(key < 0){
perror("ftok");
return -1;
}
int shmid = shmget(key, size, shmflag);
if(shmid < 0){
perror("shmget");
return -2;
}
return shmid;
}
int create_shm( int size)
{
return comm_shm(size, IPC_CREAT|IPC_EXCL|0666);
}
int get_shmid()
{
return comm_shm(0, IPC_CREAT);
}
int destroy_shm(int shmid)
{
if( shmctl( shmid, IPC_RMID, NULL) < 0)
{
perror("shmctl");
return -1;
}
return 0;
}
#include "comm.h"
int main()
{
int shmid = create_shm(4096);// 创建共享内存块
char *buf;
int i = 0;
buf = shmat(shmid,NULL, 0 );
while( i < 4096)
{
buf[i] = 'a'+i ;
i++;
sleep(1);
if(i == 26)
break; // 让程序结束,去释放该共享内存
}
destroy_shm(shmid);
return 0;
}
#include "comm.h"
int main()
{
int shmid = get_shmid(4096);
char *buf;
int index = 0;
buf = shmat(shmid,NULL, 0 );
while( index < 4096)
{
printf("%s\n", buf);
sleep(1);
index++;
if( index == 27)
break; // 让程序结束
}
return 0;
}
截图演示:
先启动服务端,我们可以执行命令ipcs -m 查看分配的该内存块:
可以发现倒数第二列,表示为1,表示该进程被attach到该内存块,等启动client进程后该值变为2
等两个进程结束后再次使用ipcs -m 命令,发现已经看不到之前申请的那块共享内存。