linux进程间通信–共享内存(POSIX 版本)
System V
共享内存模型使用的是key和标识符,这与标准的UNIX I/O模型使用文件名和描述符的做法不一致。这种差异导致System V
共享内存段需要一整套全新的系统调用和命令。由于这些潜在的缺点,因此POSIX
标准又重新定义了共享内存API。
共享内存的使用
:共享内存被映射到进程的虚拟地址空间之后,就可以跟正常的进程空间内存一样进行各种操作了。
其他说明:
- 共享内存并未提供同步机制。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量.
- 使用了POSIX共享内存函数,链接时要用到
-lrt
库. - 使用POSIX标准接口shm_open()创建的共享内存对象,可将它认为特殊的文件,可以使用文件操作的相关接口。如修改权限
fchmod()
. - Linux中一般将共享内存创建的文件放在
/dev/shm
目录下。
共享内存的相关接口函数如下:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
/*
* 创建和打开一个新的共享内存对象或打开一个已创建好的对象
* name :共享内存对象的文件名称.如`/sh_mem1`.由`/`开始,并且只能有一个`/`
* oflag:
* O_CREAT: 如果不存在则创建,如果存在在返回创建好的文件描述符。
* O_EXCL : 配合O_CREAT使用,只有不存在时才创建。存在时返回失败
* O_RDONLY:只读
* O_RDWR: 读写
* O_TRUNC: 如果共享内存对象已经存在,则将其截断为零字节。
* mode :权限。打开一个已经存在的共享内存时,权限设置为0.
* return:if true:返回文件描述符。if false:-1.errno值如下:
* EEXIST:使用了O_EXCL,且文件已经存在
* EINVAL:name名称不符合要求。
*/
#include <sys/mman.h>
int shm_unlink(const char *name);
/*
* 创建和打开一个新的共享内存对象或打开一个已创建好的对象
* name :共享内存对象的文件名称
* return:if true:返回文件描述符。if false:-1.
* ENOENT:对象不存在
*/
除了上面的两个专门的共享内存API.一般也要使用到mmap()
,fstat()
,ftruncate()
以及其他的文件操作函数。下面仅简单的介绍,详细请参考具体章节。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags,int fildes, off_t off);
/*
* 在进程的地址空间和内存对象之间建立映射。
* addr: 将文件映射到进程空间指定地址,可以为 NULL,此时系统将自动分配地址。
* length: 要映射的大小。
* prot: 映射的访问权限。
* PROT_EXEC: 页面可执行。
* PROT_READ: 页面可读。
* PROT_WRITE: 页面可写。
* PROT_NONE: 页面不可访问。
* flags:
* MAP_SHARED: 共享内存的方式。
* fildes: 文件描述符,这里为shm_open()函数返回的值。
* offset: 指定从文件的哪一部分开始映射,必须是内存页的整数倍,通常为 0。
* return:if true:返回文件描述符。if false:-1.
* ENOENT:对象不存在
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int fstat(int fd, struct stat *statbuf);
/*
* 用于查询文件的信息
* fd :文件描述符
* statbuf:用于返回的文件信息buff.
* return:if true:返回文件描述符。if false:-1.
*/
#include <unistd.h>
#include <sys/types.h>
int ftruncate(int fd, off_t length);
/*
* 将文件截断为指定长度。用于重新设置文件的大小。
* fd :文件描述符
* length:要设定的长度
* return:if true:返回文件描述符。if false:-1.
*/
共享内存创建与删除例程:
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
const char *shm_name = "/sh_mem1";
const int shm_size = 1024;
//共享内存创建,创建后在文件系统中可以找到(/dev/shm)
int shmfd = shm_open(shm_name, O_CREAT | O_RDWR, 0666);
if(shmfd == -1){
printf("shm_open error(%d)\n",errno);
exit(1);
}
//创建成功后,别的进程也可以通过"/sh_mem1"来获取共享内存。如
//int shmfd = shm_open("/sh_mem1", O_RDONLY, 0);
//2. 设置共享内存的大小
if (ftruncate(shmfd, shm_size) == -1){
printf("ftruncate error(%d)\n",errno);
exit(1);
}
//2.5 或者使用fstat获取内存大小
struct stat buf;
if(fstat(shmfd,&buf) == -1){
printf("fstat error(%d)\n",errno);
exit(1);
}
printf("shm size:(%ld)\n",buf.st_size);
//3. 映射到进程空间
char *addr = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
//4. 就可以对addr进行正常的读写操作了。如
strcmp(addr,"hello word");
//5. 关闭共享内存的文件描述符。但并不影响map后addr的操作
close(shmfd);
//6. 可以通多addr对共享内存继续操作
//7. 取消映射
munmap(addr, shm_size);
//8. 删除共享内存
if (shm_unlink(shm_name) == -1){
printf("shm_unlink error(%d)\n",errno);
exit(1);
}
exit(0);
}
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。