1. 共享内存简介
共享内存主要用于不同进程之间相互通信,因为操作的是同一块地址,不需要内核和用户层之间数据拷贝,属于最快的进程间通信方式,不过,为了防止读写冲突,一般需要额外的同步手段。之前介绍了SystemV共享内存的使用方式,今天介绍下Posix共享内存。Posix 共享内存API主要有这几个,shm_open,用于获取或者创建一个共享内存文件描述符,ftruncate,用于设置共享内存的大小,新建的共享内存大小为0,mmap,用于将共享内存文件映射到进程的虚拟地址空间,其实共享内存真正核心的工作主要在于mmap,不过mmap是另外一个话题,暂时不讨论。munmap,和mmap作用相反。shm_unlink,移除共享内存。close关闭shm_open获取的文件描述符,通常mmap之后就可以关闭了。fstat获取共享内存的信息,比如大小。下面介绍下相关API接口
2. API接口
2.1 创建共享内存
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
/**
* @brief 创建或获取已存在的共享内存
*
* @params name 共享内存名字,以斜杠开头'/',不超过NAME_MAX(255)字符
* @params oflag O_RDONLY| O_RDWR| O_CREAT| O_EXCL| O_TRUNC
* @params mode 设置一些权限,通常使用0即可
*
* @returns 成功返回文件描述符,失败返回-1
**/
int shm_open(const char *name, int oflag, mode_t mode);
2.2 设置共享内存大小
#include <unistd.h>
#include <sys/types.h>
/**
* @brief 修改fd的大小
*
* @returns 成功返回0,失败返回-1
**/
int ftruncate(int fd, off_t length);
2.3 查询共享内存文件大小
#include <unistd.h>
#include <sys/types.h>
/**
* @brief 查询fd的信息
* @param fd 要查询的文件描述符
* @param buf 用于保存查询结果的buffer
* @returns 成功返回0,失败返回-1
**/
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
int fstat(int fd, struct stat *buf);
2.4 映射共享内存
#include <sys/mman.h>
/**
* @brief 映射共享内存到进程自身虚拟地址空间
*
* @params addr 映射到的地址,通常设置为NULL,表示内核自己选择合适地址。
* @params length 映射的共享内存大小
* @params prot 保护标志位,可选PROT_EXEC| PROT_READ| PROT_WRITE| PROT_NONE
* @params flags 可选
* MAP_SHARED,共享模式,修改对其它进程可见
* MAP_PRIVATE 私有映射,对共享内存的修改对其它进程不可见。
* @params fd 被映射的共享内存文件描述符
* @params offset 文件偏移值,通常填0
* @returns 成功返回共享内存地址,失败返回-1
**/
void *mmap(void *addr, size_t length,int prot, int flags ,
int fd, off_t offset);
/**
* @brief 删除mmap的映射
*
* @params addr 映射到的地址
* @params length 映射的共享内存大小
* @returns 成功返回0址,失败返回-1
**/
int munmap(void *addr, size_t length);
2.5 删除共享内存
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
/**
* @brief 删除共享内存,只有当所有进程都取消映射后才销毁它
*
* @params name 共享内存名字
*
* @returns 成功返回0,失败返回-1
**/
int shm_unlink(const char *name);
# 编译加上 -lrt选项
Link with -lrt.
3. 共享内存示例
这里仅简单读写,没有考虑同步。
3.1 写共享内存
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <error.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
#define SHM_NAME "/shm0"
#define SHM_SIZE 4096
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("Usage: ./shm_write msg\n");
return -1;
}
// 创建或打开已存在的共享内存
int shm_fd = shm_open(SHM_NAME, O_RDWR|O_CREAT, 0);
if (shm_fd == -1)
{
perror("shm_open error");
return -1;
}
// 设置共享内存大小
if (ftruncate(shm_fd, SHM_SIZE))
{
perror("ftruncate error");
return -1;
}
// 映射共享内存
char* buffer = (char *)mmap(NULL, SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (buffer == (char *)MAP_FAILED)
{
perror("mmap error");
return -1;
}
// 关闭共享内存文件描述符,并不影响映射
close(shm_fd);
// 写共享内存
strncpy(buffer, argv[1], strlen(argv[1]));
// 删除共享内存
//shm_unlink(shm_fd);
return 0;
}
3.2 读共享内存
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <error.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define SHM_NAME "/shm0"
#define SHM_SIZE 4096
int main()
{
// 创建或打开共享内存
int shm_fd = shm_open(SHM_NAME, O_RDWR|O_CREAT, 0);
if (shm_fd == -1)
{
perror("shm_open error");
return -1;
}
// 获取共享内存大小,读取的时候要用
struct stat stat={0};
if (fstat(shm_fd, &stat))
{
perror("fstat error");
return -1;
}
// 映射共享内存,大小为上一步获取的大小
char* buffer = (char*)mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, shm_fd, 0);
if (buffer == (char *)MAP_FAILED)
{
perror("mmap error");
return -1;
}
// 关闭文件描述符
close(shm_fd);
// 读取共享内存
printf("Read Msg:%s\n", buffer);
// 删除共享内存
shm_unlink(SHM_NAME);
return 0;
}
3.3 编译&运行
default:
gcc -o shm_read shm_read.c -lrt
gcc -o shm_write shm_write.c -lrt
clean:
rm -rf shm_write shm_read
4. 参考文档
https://www.man7.org/linux/man-pages/man7/mq_overview.7.html
================================================================================================
Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。...