linux进程通信之共享内存

共享内存是被多个进程共享的一部分物理内存。
共享内存是进程间共享数据一种最快的方法,一个进程向共享区域写入数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

共享内存实现两个步骤:
1.创建共享内存,使用shmget函数。
2.映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。
3.当进程空间不需要共享内存时,把它从进程空间脱离

转至:http://blog.csdn.net/lanmanck/article/details/6092995
创建共享内存
int shmget (key_t key,int size,int shmflg)
key: 标识符的规则
size:共享存储段的字节数
flag:读写的权限
返回值:成功返回共享存储的id,失败返回-1

key_t key

key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
在IPC(InterProcess Communication)的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。
通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。

int shmflg
int size(单位字节Byte)

size是要建立共享内存的长度。所有的内存分配操作都是****以页为单位的**。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整**一页**(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。

int shmflg

shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL    只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读取和写入许可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。

需要注意的是,使用参数要加上 | 0666 作为校验,在有些Linux系统中,如果不加此校验,则不能顺利获取共享空间的值(如Ubuntu)。此外,有两个常用参数,一般要同时出现,他们是:S_IRUSH | S_IWUSR 。由于这两个参数非常常用,程序员一般做这样的操作
  #define PERM S_IRUSR | S_IWUSR | IPC_CREAT
  这样一来,第三个参数就可以直接用PERM来表示了!
  
返回值

成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
EINVAL 参数size小于SHMMIN或大于SHMMAX。
EEXIST 预建立key所致的共享内存,但已经存在。
EIDRM 参数key所致的共享内存已经删除。
ENOSPC 超过了系统允许建立的共享内存的最大值(SHMALL )。
ENOENT 参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
EACCES 没有权限。
ENOMEM 核心内存不足。

struct shmid_ds

shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。

include/linux/shm.h

struct shmid_ds {
    struct ipc_perm    shm_perm;      /* operation perms */
    int                shm_segsz;     /* size of segment (bytes) */
    __kernel_time_t    shm_atime;     /* last attach time */
    __kernel_time_t    shm_dtime;     /* last detach time */
    __kernel_time_t    shm_ctime;     /* last change time */
    __kernel_ipc_pid_t shm_cpid;      /* pid of creator */
    __kernel_ipc_pid_t shm_lpid;      /* pid of last operator */
    unsigned short     shm_nattch;    /* no. of current attaches */
    unsigned short     shm_unused;    /* compatibility */
    void               *shm_unused2; /* ditto - used by DIPC */
    void               *shm_unused3; /* unused */
};

struct ipc_perm

对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。

struct ipc_perm {
    __kernel_key_t   key;
    __kernel_uid_t   uid;
    __kernel_gid_t   gid;
    __kernel_uid_t   cuid;
    __kernel_gid_t   cgid;
    __kernel_mode_t mode;
    unsigned short   seq;

};
//----------------------------------------
映射共享内存
shmat
void *shmat(int shmid, const void *addr, int flag);
shmid:共享存储的id
addr:一般为0,表示连接到由内核选择的第一个可用地址上,否则,如果flag没有指定SHM_RND,则连接到addr所指定的地址上,如果flag为SHM_RND,则地址取整
flag:如前所述,一般为0
返回值:如果成功,返回共享存储段地址,出错返回-1
共享存储器的执行方式是将一个储存器区段标记为共用,这时各进程可以把这个区段映射到该进程本身的虚拟地址里。建立共享存储器可通过shmget系统调用,shmget执行后,核心程序就保留一块指定大小的空间,同时关于此共享存储器的一切数据,如区段的长度,区段的存取权,区段建立者的进程识别码等存入一个叫shmid_ds的结构。现在共享存储器虽然已经建立了,可是仍无法连上它,这时就须通过shmat系统调用得到一个指向共享存储器基址的指针,通过此指针,就可以如同于操作一般存储器似的取用共享存储器。shmdt进行相反的工作,用来脱离已连上的共享存储器。
//脱离进程空间
shmdt
int shmdt(void *addr);
addr:共享存储段的地址,以前调用shmat时的返回值
shmdt将使相关shmid_ds结构中的shm_nattch计数器值减1

#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<errno.h>
#include<stdio.h>

/* 共享内存读写 */
#define PERM S_IRUSR|S_IWUSR

int main(int argc,char *argv[])
{
int shmid;
char *p_addr,*c_addr;
pid_t pid;

if(2!=argc)
{
	fprintf(stderr,"usage:%s\n",argv[0]);
	error(1);
}

/* 创建共享内存 */
shmid = shmget(IPC_PRIVATE,1024,PERM);
if(-1 == shmid)
{
	fprintf(stderr,"create share memory error :%s\n",strerror(errno));
	error(1);
}

/* 创建子进程 */
pid = fork();
/* 子进程 */
if(0 == pid)
{
	sleep(1); //休眠1s,先让父进程运行
	c_addr = shmat(shmid,NULL,0);
	printf("share memory is %s \n",c_addr);
	exit(0);
}
/* 父进程 */
else if(pid > 0)
{	
	p_addr = shmat(shmid,NULL,0);
	memset(p_addr,'\0',1024);
	strncpy(p_addr,argv[1],1024);
	wait(NULL);
	exit(0);	
}
else if(-1 == pid)	
{
	fprintf(stderr,"fork create error :%s\n",strerror(errno));
	error(1);
}

}

运行结果:./shmem hellofriend
share memory is hellofriend

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值