Linux共享内存
一、基本概念
linux下,每个进程都有自己拥有的内存区域,进程的内存总是私有的。共享内存是从系统的空闲内存池中分配的,希望访问它的每个进程连接它。这个连接过程称为映射。映射后,每个进程都可通过访问自己的内存而访问共享内存区域,从而与其他进程进行通信。
二、常用函数
1. shm_open
// 用于创建或者打开共享内存文件,linux系统默认存放目录位/dev/shm
int shm_open(const char *name, int oflag, mode_t mode);
name: 共享文件名
oflag: 打开文件的操作属性,O_CREAT、O_RDWR、O_EXCL的按位或运算组合
mode: 文件共享模式,如0777
返回值: 成功返回fd>0, 失败返回fd<0
2. mmap
// 将打开的文件映射到内存,从而可以用操作内存指针的方式来操作文件数据
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
addr: 要将文件映射到内存中的地址,一般为NULL,由Linux内核来指定
length: 需要映射文件数据的长度
prot: 映射内存区域的操作权限,包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标志位参数,包括:MAP_SHARED、MAP_PRIVATE与MAP_ANONYMOUS。
MAP_SHARED: 建立共享,用于进程间通信,如果没有这个标志,则别的进程即使能打开文件,也看不到数据。
MAP_PRIVATE: 只有进程自己用的内存区域
MAP_ANONYMOUS:匿名映射区
fd: 建立映射区的文件描述符,shm_open或者open打开的文件
offset: 映射文件相对于文件头的偏移位置,应该按4096字节对齐
返回值:成功返回映射的内存地址指针,可以用这个地址指针对映射的文件内容进行读写操作,读写文件数据如同操作内存一样;如果 失败则返回NULL。
3. munmap
// 取消内存映射,将映射的内存从进程地址空间撤销;不调用,在进程终止之前无法释放该内存区域
int munmap(void *addr, size_t length);
addr: 由mmap成功返回的地址
length: 要取消的内存长度
4. shm_unlink
// 删除有shm_open或者open打开的共享文件,在/dev/shm路径下
int shm_unlink(const char *name);
name: 共享文件名
5. ftruncate
// 重置文件大小
int ftruncate(int fd, off_t length);
fd:shm_open返回打开的文件描述符
length:重置大小
三、使用示例
writer.c函数实现,往/dev/shm目录文件写数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define MMAP_DATA_SIZE 1024
#define USE_MMAP 1
struct shm_data
{
int start;
int end;
char name[256];
};
int main(int argc,char * argv[])
{
struct shm_data* data;
int fd = shm_open("shm-file0001", O_CREAT|O_RDWR, 0777);
if (fd < 0) {
printf("shm_open failed!\n");
return -1;
}
printf("writer fd is %d\n", fd);
ftruncate(fd, MMAP_DATA_SIZE);
if (USE_MMAP) {
data = (struct shm_data*)mmap(NULL, sizeof(struct shm_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (!data) {
printf("mmap failed\n");
close(fd);
}
printf("writer data addr is %d\n", data);
printf("before, shm data start is %d\n", data->start);
data->start = 101;
data->end = 10000001;
printf("after, shm start is %d, end is %d\n", data->start, data->end);
sprintf(data->name, "This is a share memory! %d\n", fd);
munmap(data, MMAP_DATA_SIZE);
}
else {
char buf[1024];
int len = sprintf(buf,"This is a share memory by write! ! %d\n",fd);
if (write(fd, buf, len) <= 0) {
printf("write file %d failed!%d\n",len,errno);
}
}
close(fd);
getchar();
shm_unlink("shm-file0001");
return 0;
}
reader.c函数实现,从/dev/shm目录文件写数据
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
struct shm_data
{
int start;
int end;
char name[256];
};
#define MMAP_DATA_SIZE 1024
int main(int argc,char * argv[])
{
struct shm_data* data;
int fd = shm_open("shm-file0001", O_RDWR, 0777);
if(fd < 0)
{
printf("error open shm object\n");
return -1;
}
data = (struct shm_data*)mmap(NULL, MMAP_DATA_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (!data) {
printf("mmap failed!\n");
close(fd);
return -1;
}
printf(data->name);
munmap(data,MMAP_DATA_SIZE);
close(fd);
getchar();
return 0;
}
四、share内存不足解决方法
# 查看大小
df -h /dev/shm
# 系统默认大小修改
sudo vim /etc/fstab
tmpfs /dev/shm tmpfs defaults,size=8192M 0 0
# 刷新/etc/fstab的内容
mount -a
# 查看挂载点
cat /etc/mtab | grep /dev/shm
mount -l | grep /dev/shm
# 临时修改
sudo mount -o remount,size=8192M /dev/shm