共享内存(sharedmemory)
概述
共享内存是进程间通信中最简单的一种,也是最快的一种,共享内存就是一段能被其他进程所共同访问的物理内存,这段共享内存由一个进程创建,但多个进程都可以访问
优点
因为所有进程共享同一块内存,所以共享内存在各种进程通信方式中具有最高的效率,访问共享内存区域和访问进程独有的内存区一样快,不需要通过其他方式来完成,所以避免了对数据的不必要复制
缺点
因为所有进程共享内存,就会引起同步的问题,而系统对访问共享内存没有进行同步机制,就需要用户自己来提供同步措施
共享内存使用步骤
- 创建新的共享内存
从内存中申请一段共享内存区域,所有的进程都共享对同一内存的访问,但只应由一个进程来创建新的共享内存。
再次使用一块已经存在的内存块不会创建新内存,而是会返回一个标识该内存块的标识符。
- 映射共享内存到各进程的地址空间
一个进程如果需要使用这个共享内存块,首先必须将它映射到自己的地址空间,这样会创建一个从进程虚拟地址空间到共享内存页面的映射关系
注:所有的共享内存块的大小都必须时系统页面大小的整数倍,系统页面大小指的是系统中单个内存页面包括的字节数,在Linux系统中,内存页面大小是4kb,用户可以通过调用
getpagesize()
函数获取这个值。
- 使用共享内存进行数据交换
经过映射后,进程就可以通过映射的地址来访问这段共享内存。
实现方式:
strcpy
、memcpy
、memset
、bzero
等
- 断开与共享内存的映射
当进程对共享内存的使用结束以后,这个映射关系将被删除,所以进程需要断开与共享内存的映射。
- 回收共享内存
当没有进程需要使用共享内存的时候,必须要有一个进程负责将这个共享内存快释放,以便系统回收。
共享内存相关函数
有两类API函数用于在进程间共享内存:
System V 和POSIX
,这两类函数在使用上遵从的原则相同,核心思想就是任何要被共享的内存都必须经过显示的分配。
System V
shmget
调用头文件
#include <sys/ipc.h>
#include <sys/shm.h>
函数原型
int shmget(key_t key, size_t size, int shmflg);
功能
如果有则得到一个共享内存的描述符,如果无则创建一个共享内存对象并返回标识符
参数
-
key:通常来源于ftok返回的IPC值,如果key为(IPC_PRIVATE)则会创建新的共享内存
-
ftok:
key_t ftok(const char *pathname, int proj_id);
,传入的路径必须是存在的路径且可访问 -
size:创建的共享内存大小,以字节为单位,一般使用
getpagesize()
函数来获取页大小。 -
shmflg:
当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符。
如果shmflg中包含了IPC_CREAT和IPC_EXCL,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在则报错,可以通过此方式来验证共享内存是否已经存在。shmflg也用来决定共享内存的访问权限。
返回值
成功返回共享内存标识符,失败返回**-1**,提供错误码
shmat
调用头文件
#include <sys/types.h>
#include <sys/shm.h>
函数原型
void *shmat(int shmid,const void *shmaddr, int shmflg);
功能
连接共享内存,成功后会将共享内存映射到调用进程的地址空间
参数
-
shmid:共享内存的标识符
-
shmaddr:指定共享内存映射到地址空间的什么位置,可指定NULL让内核自己决定一个合适的地址位置
-
shmflg:输入以下内容,则为特定功能,输入其他则为读写模式,一般写0
/* Flags for `shmat’. */
#define SHM_RDONLY 010000
/* attach read-only else read-write */#define SHM_RND 020000
/* round attach address to SHMLBA */#define SHM_REMAP 040000
/* take-over region on attach */#define SHM_EXEC 0100000
/* execution access */
返回值
成功返回已连接的地址,否则返回(void *)-1
,提供错误码
shmdt
调用头文件
#include <syt/types.h>
#include <sys/shm.h>
函数原型
int shmdt(const void *shmaddr);
功能
断开之前连接的共享内存
参数
- shmaddr:连接的共享内存起始地址
返回值
成功返回0,否则返回**-1**,提供错误码
shmctl
调用头文件
#include <sys/types.h>
#include <sys/shm.h>
函数原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能
控制共享内存的运行
参数
-
shmid:共享内存的标识符
-
cmd:操作模式
/* Control commands for
msgctl
,semctl
, andshmctl
. */#define IPC_RMID 0
/* Remove identifier. */#define IPC_SET 1
/* Set `ipc_perm’ options. */#define IPC_STAT 2
/* Get `ipc_perm’ options. *//* Commands for `shmctl’. */
#define SHM_LOCK 11
/* lock segment (root only) */#define SHM_UNLOCK 12
/* unlock segment (root only) */ -
buf:指向
struct shmid_ds
结构体的指针
返回值
成功返回0,否则返回**-1**,提供错误码
//shm.h /* Data structure describing a shared memory segment. */ struct shmid_ds { struct ipc_perm shm_perm; /* operation permission struct */ size_t shm_segsz; /* size of segment in bytes */ __time_t shm_atime; /* time of last shmat() */ __time_t shm_dtime; /* time of last shmdt() */ __time_t shm_ctime; /* time of last change by shmctl() */ __pid_t shm_cpid; /* pid of creator */ __pid_t shm_lpid; /* pid of last shmop */ shmatt_t shm_nattch; /* number of current attaches */ __syscall_ulong_t __glibc_reserved4; __syscall_ulong_t __glibc_reserved5; };
函数实例
- 创建共享内存
#include "head.h"
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_SHM_NAME, PROJ_ID);
// 1.创建共享内存
int shmid = shmget(key, getpagesize() * 2, IPC_CREAT | IPC_EXCL | 0777);
if (shmid == -1)
{
perror("shmget failed");
return -1;
}
printf("shmid=%d\n",shmid);
return 0;
}
- 向共享内存发送数据
#include "head.h"
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_SHM_NAME,PROJ_ID);
//1.得到编号共享内存
int shmid = shmget(key,getpagesize()*2,0777);
if (shmid == -1)
{
perror("shmget failed");
return -1;
}
printf("shmid=%d\n",shmid);
//2.映射共享内存
void *p = shmat(shmid,NULL,0);
if (p == (void *)-1)
{
perror("shmat failed");
return -1;
}
//3.操作共享内存
//定义要写到内存中的字符串
//循环
char data[30] = "hello, I'm write";
int i = 0;
while (1)
{
char buf[100] = {0};
snprintf(buf,100,"%s--%d\n",data,i);
memcpy(p,buf,strlen(buf));
printf("no.%d--%s",i,buf);
if (i >= 30)
{
break;
}
i++;
sleep(1);
}
//4.解除映射
int r2 = shmdt(p);
if (r2 == -1)
{
perror("shmdt failed");
return -1;
}
return 0;
}
- 从共享内存读取数据
#include "head.h"
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_SHM_NAME, PROJ_ID);
// 1.创建共享内存
int shmid = shmget(key, getpagesize() * 2, 0777);
if (shmid == -1)
{
perror("shmget failed");
return -1;
}
printf("shmid=%d\n",shmid);
// 2.映射共享内存
void *p = shmat(shmid, NULL, 0);
if (p == (void *)-1)
{
perror("shmat failed");
return -1;
}
// 3.操作共享内存
int i = 0;
while (1)
{
char buf[100] = {0};
memcpy(buf, p, strlen(p));
printf("第%d次:%s",i,buf);
i++;
if (i >= 60)
{
break;
}
sleep(1);
memset(buf,0,sizeof(buf));
}
// 4.解除映射
int r2 = shmdt(p);
if (r2 == -1)
{
perror("shmdt failed");
return -1;
}
return 0;
}
- 删除共享内存
#include "head.h"
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_SHM_NAME, PROJ_ID);
// 得到共享内存
int shmid = shmget(key, getpagesize() * 2, 0777);
if (shmid == -1)
{
perror("shmget failed");
return -1;
}
printf("shmid=%d\n",shmid);
int r = shmctl(shmid,IPC_RMID,NULL);
return 0;
}
POSIX
shm_open
调用头文件
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
函数原型
int shm_open(const char *name, int oflag, mode_t mode);
功能(类似于open)
创建并打开一个新的或者打开一个已经存在的POSIX共享内存区域。
参数(类似于open)
-
name:共享内存对象的标识名
-
oflag:与open不一样的是参数中没有O_WRONLY
- O_RDONLY:只读
- O_RDWR:读写
- O_CREAT:如果没有,则创建
- O_EXCL:与O_CREAT一起使用,如果存在,则报错返回
- O_TRUNC:截断
-
文件共享模式:权限,例如0777
返回值
成功返回文件描述符,失败返回**-1**,提供错误码
mmap / munmap
调用头文件
#include <sys/mman.h>
函数原型
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
功能
映射/解除映射 文件存储空间到进程的虚拟地址空间
参数
-
addr:映射的地址空间首地址(一般填NULL:表示让系统决定)
-
length:地址空间大小
-
prot:映射的地址空间访问方式,必须和文件打开方式匹配
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
-
flags:映射的地址空间的访问标记
常用以下两种
-
MAP_SHARED Share this mapping.
-
MAP_PRIVATE Create a private copy-on-write mapping.
-
-
fd:需映射的文件描述符
-
offset:文件存储空间的偏移量
返回值
成功,munmap返回0,mmap返回映射后的地址;如果错误,则返回MMAP_FAILED,提供错误码(可以使用perror)
shm_unlink
调用头文件
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
函数原型
int shm_unlink(const char *name);
功能
通过共享内存对象名称删除一个共享内存
参数
- name:共享内存名
返回值
成功返回0,失败返回**-1**,提供错误码。