共享内存可以说是最有用的进程间的通信方式,也是最快的IPC形式。共享内存只需要凉席数据拷贝:一次是从输入文件到共享内存区;另一次从共享区到输出文件。进程之间共享内存时,并不是读写少量的数据后就解除映射,而是在所有的通信结束之后才解除映射。这样,数据内容一直保持在共享内存中,并没有进行写入文件。共享内存中的内容往往是在解除映射时才写回文件。因此,共享内存的通信是非常高效的。
Linux传统的文件访问方式:
如果有多个进程访问同一个文件,则每一个进程在自己的内存空间都包含有该文件的副本。如下图所示:
上图说明了两个进程同事读一个文件的同意页的情况,系统要将该页从磁盘读到告诉缓冲区中,每一个进程再执行一个存储器内的复制操作将数据从高速缓冲区中读到自己的地址空间。
共享存储映射下的文件访问:
shmget()、shmat()、shmdt()、shmctl()实现共享内存:
错误代码:EINVAL:参数size小于SHMMIN或大于SHMMAX;EEXIST:预建立key所指的共享内存,但已经存在;EIDRM:参数key所指的共享内存已经删除;ENOSPC:超过了系统允许建立的共享内存的最大值(SHMALL);ENOENT:参数key所指的共享内存不存在,而参数shmflg未设IPC_CREAT位;EACCES:没有权限;ENOMEM:核心内存不足
void shmat(int shmid,const void *shmaddr,int shmflg):空间映射。通过创建的共享内存,在它能被进程访问之前,需要把该段映射到用户空间。shmid标识唯一的共享内存;shmaddr用来指定共享内存映射到当前进程中的地址位置 ,当shmflg设置为SHM_RND标志时该设置有效;大部分情况下设置为NULL,让系统自动选择地址,从而减小程序对硬件的依赖。shmflg还可以设置为SHM_RDONLY,使映射过来的地址只读。如果函数调用成功返回映射的地址的第一个字节,否则返回-1。
错误代码:EACCES:无权限以指定方式连接共享内存;EINVAL:无效的参数shmid或shmaddr;ENOMEM:核心内存不足。
错误代码:EINVAL:无效的参数shmaddr
int shmctl(int shmid,int cmd,struct shmid_ds *buf):控制共享内存。cmd为控制命令标志,分别为:IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中;IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构中; IPC_RMID:删除共享内存;用于把struct shmid_ds定义在include/linux/shm.h。成功返回0,失败返回-1。
错误代码:EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存;EFAULT:参数buf指向无效的内存地址;EIDRM:标识符为shmid的共享内存已被删除;EINVAL:无效的参数cmd或shmid;EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行。
示例程序:
server.c程序先运行,改程序先挂接到共享内存上,然后每隔一秒钟去访问该共享内存中的数据,然后打印到屏幕上。
server.c
#include "comm.h"
int main(){
int shm_id = CreatShm();
if(shm_id < 0){
printf("CreatShm failed!\n");
return -1;
}
char *buf = (char *)attch(shm_id);
while(1){
printf("%s\n",buf);
sleep(1);
}
dattch(buf);
DestoryShm(shm_id);
return 0;
}
client.c
#include "comm.h"
int main(){
int shm_id = GetShm();
if(shm_id < 0){
printf("GetShm failed!\n");
return -1;
}
char *buf = (char *)attch(shm_id);
int count = 0;
while(1){
buf[count++] = 'A';
buf[count] = '\0';
sleep(1);
}
return 0;
}
comm.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <error.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define _SIZE_ 4096
#define _FILE_PATH_ "/home/flr/flr/code/main.c"
#define _PROJ_ID_ 0x55555
int CreatShm();
int GetShm();
int DestoryShm(int shm_id);
void *attch(int shm_id);
int dattch(void *addr);
comm.c
#include "comm.h"
static int CommCreatShm(int flg){
key_t _key = ftok(_FILE_PATH_,_PROJ_ID_);
if(_key < 0){
perror("ftok");
return -1;
}
printf("_key = %d\n",_key);
int shm_id = shmget(_key,_SIZE_,flg);
if(shm_id < 0){
perror("shm_id");
return -1;
}
return shm_id;
}
int CreatShm(){
int flg = IPC_CREAT|IPC_EXCL|0644;
return CommCreatShm(flg);
}
-int GetShm(){
int flg = IPC_CREAT;
return CommCreatShm(flg);
}
int DestoryShm(int shm_id){
if(shmctl(shm_id,IPC_RMID,NULL) == -1){
perror("shmctl");
return -1;
}
return 0;
}
void *attch(int shm_id){
return shmat(shm_id,NULL,0);
}
int dattch(void *addr){
return shmdt(addr);
}