一, linux共享内存介绍
共享内存机制
是允许两个或多个进程(不相关或有亲缘关系)访问同一个逻辑内存的机制。它是共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。
两种常用共享内存方式
1, System V版本的共享内存 shmm
多个进程直接共享内存
2, 文件映射 mmap
- 文件进行频繁读写,将一个普通文件映射到内存中
- 将特殊文件进行匿名内存映射,为关联进程提供共享内存空间
- 为无关联的进程提供共享内存空间,将一个普通文件映射到内存中
基本操作
查看共享内存
Ipcs
操作共享内存
ipcrm -m
ipcrm:选项需要一个参数 – m
ipcrm: illegal option – ?
usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
[-Q msgkey] [-M shmkey] [-S semkey] … ]
案例:第1个应用程序读共享内存,第2个应用程序写共享内存;第3个删除共享内存。会出现什么现象。
Linux内核管理共享内存的机制
命令:
ipcs -m
二, linux的共享内存的api介绍
1. ftok函数生成key标识符
key_t ftok(const char *pathname,int proj_id)
2. 创建一个共享内存块,返回这个共享内存块的标识符shmid
int shmget(key_t key,size_t size,int shmflg)
参数说明:size - 申请的共享内存的大小,为4k的整数倍;
shmflg - IPC_CREAT 创建新的共享内存,已存在 使用IPC_EXCL
3.挂接共享内存(将进程地址空间挂接到物理空间,可以有多个挂接)
void *shmat(int shmid,const void *shmaddr, int shmflg)
参数说明:shmid - 挂接的共享内存ID.
shmaddr - 一般为0,表示连接到由内核选择的第一个可用地址上
shmflg - 一般为0
4.取消共享内存映射
int shmdt(const void *shmaddr);
5.用于控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数 shmid - 由shmget返回的共享内存标识码
cmd - 将要采取的动作(可取值:IPC_STAT、IPC_SET、IPC_RMID)
buf - 指向一个保存着共享内存的模式状态和访问权限的数据结构
三, 写一个简单例子
一个写共享内存, 一个读取共享内存
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main()
{
void *shm = NULL;
int shmid = 0, i = 0;
struct Conn_stat stat = {0,"陈松爱陈丽"};
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct Conn_stat), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(1);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, (void*)0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(2);
}
printf("Memory attached at %p\n", shm);
//设置共享内存
struct Conn_stat *p = (struct Conn_stat*)shm;
memcpy(p,&stat,sizeof(struct Conn_stat));
while((i++) < 30)//修改共享内存中写数据
{
p->count++;
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(3);
}
exit(0);
}
读取共享内存例子
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
//#include "shmdata.h"
#include <string.h>
#include <errno.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main()
{
void *shm = NULL;//分配的共享内存的原始首地址
struct Conn_stat *stat = NULL;//指向shm
int shmid;//共享内存标识符
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct Conn_stat), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(0);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(1);
}
printf("\nMemory attached at %p\n", shm);
//设置共享内存
stat = (struct Conn_stat*)shm;
int i = 0;
while((i++) < 10)
{
printf("ip = %s ,count: %d\t\t\n", stat->ip, stat->count);
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(2);
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed, reason: %s\n",strerror(errno));
exit(3);
}
exit(0);
}
makefile
.SUFFIXES: .c .o
CC = gcc
SRCS1 = shmwrite.c
SRCS2 = shmread.c
OBJS1 = $(SRCS1:.c = .o)
OBJS2 = $(SRCS2:.c = .o)
EXE1 = shmwrite
EXE2 = shmread
#$(OBJS2)
all: $(OBJS1) $(OBJS2)
$(CC) -o $(EXE1) $(OBJS1) -Wall -g
$(CC) -o $(EXE2) $(OBJS2) -Wall -g
@echo '^_^ ^_^ 陈丽 ^_^ ^_^'
#模式匹配
%.c%.o:
$(CC) -Wall -g -o $@ -c $^
#clean 清空二进制文件
clean:
-rm -f $(OBJS)
-rm -f core*
四, 共享内存的api的设计
// myipc_shm.h
#ifndef _WBM_MY_SHM_H_
#define _WBM_MY_SHM_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
//共享内存错误码
#define MYIPC_OK 0 //正确
#define MYIPC_ParamErr 301 //输入参数失败
#define MYIPC_NotEXISTErr 302 //共享内存不存在错误
#define MYIPC_CreateErr 303 //创建共享内存错误
//创建共享内存 若共享内存不存在,则创建
int IPC_CreatShm(int key, int shmsize, int *shmhdl);
//打开共享内存 若共享内存不存在,返回错误
int IPC_OpenShm(int key, int shmsize, int *shmhdl);
/***********************************************************************
功能描述: 关联共享内存
参数说明: shmhdl [in] 共享的句柄
mapaddr [out] 共享内存首地址
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_MapShm(int shmhdl, void **mapaddr);
/***********************************************************************
功能描述: 取消共享内存关联
参数说明: unmapaddr [in] 共享内存首地址
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_UnMapShm(void *unmapaddr);
/***********************************************************************
功能描述: 删除共享内存
参数说明: shmhdl [in] 共享的句柄
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_DelShm(int shmhdl);
/***********************************************************************
功能描述: 创建共享内存 通过种子文件
参数说明: shmname [in] 是共享内存名,系统中唯一标志
shmsize [in] 是要创建的共享内存的大小;
shmhdl [out] 共享内存的句柄.
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_CreatShmBySeedName(const char *shmname, int shmsize, int *shmhdl);
#ifdef __cplusplus
}
#endif
#endif
实现
#define _OS_LINUX_
#if defined _OS_LINUX_
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <memory.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include "myipc_shm.h"
#endif
int shmflag = 0;
int shmkey;
//创建共享内存 若共享内存不存在,则创建 若存在使用原来的
int IPC_CreatShm(int key, int shmsize, int *shmhdl)
{
int tmpshmhdl = 0;
int ret = 0;
// 创建共享内存
// 若共享内存不存在则创建
// 若共享内存已存在使用原来的
tmpshmhdl = shmget(key, shmsize, IPC_CREAT|0666);
if (tmpshmhdl == -1) //创建失败
{
ret = MYIPC_ParamErr;
printf("func shmget() err :%d ", ret);
return ret;
}
*shmhdl = tmpshmhdl;
return ret;
}
//打开共享内存 若共享内存不存在,返回错误
//参数 无意义 可填写0
int IPC_OpenShm(int key, int shmsize, int *shmhdl)
{
int tmpshmhdl = 0;
int ret = 0;
// 创建共享内存
// 若共享内存不存在则创建
// 若共享内存已存在使用原来的
tmpshmhdl = shmget(key, 0, 0);
if (tmpshmhdl == -1) //打开失败
{
ret = MYIPC_NotEXISTErr;
//printf("func shmget() err :%d ", ret);
return ret;
}
*shmhdl = tmpshmhdl;
return ret;
}
/***********************************************************************
功能描述: 创建共享内存
参数说明: shmname [in] 是共享内存名,系统中唯一标志
shmsize [in] 是要创建的共享内存的大小;
shmhdl [out] 共享内存的句柄.
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_CreatShmBySeedName(const char *shmseedfile, int shmsize, int *shmhdl)
{
if(shmflag == 0) //判断接口中共享内存key是否已经存在
{
shmkey = ftok(shmseedfile, 'c');
if (shmkey == -1)
{
perror("ftok");
return -1;
}
shmflag = 1;
}
//创建共享内存
*shmhdl = shmget(shmkey,shmsize,IPC_CREAT|0666);
if (*shmhdl == -1) //创建失败
return -2;
return 0;
}
/***********************************************************************
功能描述: 关联共享内存
参数说明: shmhdl [in] 共享的句柄
mapaddr [out] 共享内存首地址
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int
IPC_MapShm(int shmhdl, void **mapaddr)
{
void *tempptr = NULL;
//连接共享内存
tempptr = (void *)shmat(shmhdl,0,SHM_RND);
if ((int)tempptr == -1) //共享内存连接失败
return -1;
*mapaddr = tempptr; //导出共享内存首指针
return 0;
}
/***********************************************************************
功能描述: 取消共享内存关联
参数说明: unmapaddr [in] 共享内存首地址
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_UnMapShm(void *unmapaddr)
{
int rv;
//取消连接共享内存
rv = shmdt((char *)unmapaddr);
if (rv == -1) //取消连接失败
return -1;
return 0;
}
/***********************************************************************
功能描述: 删除共享内存
参数说明: shmhdl [in] 共享的句柄
返回值: 返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_DelShm(int shmhdl)
{
int rv;
//删除共享内存
rv = shmctl(shmhdl,IPC_RMID,NULL);
if(rv < 0) //删除共享内存失败
return -1;
return 0;
}
五, mmap共享文件映射
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选
定地址,映射成功后返回该地址。
参数length:代表将文件中多大的部分映射到内存。
参数prot:映射区域的保护方式。可以为以下几种方式的组合:
PROT_EXEC 执行
PROT_READ 读取
PROT_WRITE 写入
PROT_NONE 不能存取
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<string.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main(int argc,char *argv[]) //这个进程用于创建映射区进行写。
{
if(argc != 2)
{
printf("Usage: %s file.\n",argv[0]);
exit(1);
}
struct Conn_stat stat = {0,"陈松爱陈丽 --- 杨艳"};
int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0644);
if(fd < 0)
{
perror("open");
exit(2);
}
ftruncate(fd,sizeof(struct Conn_stat));
struct Conn_stat *p = (struct Conn_stat*)mmap(NULL,sizeof(struct Conn_stat),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//创建一个结构体大小的共享映射区。共享映射区我们可以当做数组区看待。
if(p == MAP_FAILED)
{
perror("mmap");
exit(3);
}
close(fd); //关闭不用的文件描述符。
memcpy(p,&stat,sizeof(struct Conn_stat));
while(1)
{
p->count++;
sleep(1);
}
int ret = munmap(p,sizeof(struct Conn_stat));
if(ret < 0)
{
perror("mmumap");
exit(4);
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/mman.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main(int argc,char *argv[]) //这个进程创建映射区进行读
{
if(argc != 2)
{
printf("Usage: %s file.\n",argv[0]);
exit(1);
}
int fd = open(argv[1],O_RDONLY,0644);
if(fd < 0)
{
perror("open");
exit(2);
}
struct Conn_stat stat;
struct Conn_stat *p = (struct Conn_stat*)mmap(NULL,sizeof(struct Conn_stat),PROT_READ,MAP_SHARED,fd,0);
if(p == MAP_FAILED)
{
perror("mmap");
exit(3);
}
close(fd);
int i = 0;
while((i++) < 10)
{
printf("ip = %s ,count: %d\t\t\n",p->ip,p->count);
sleep(1);
}
int ret = munmap(p,sizeof(stat));
if(ret < 0)
{
perror("mmumap");
exit(4);
}
return 0;
}