-
多个进程访问同一个逻辑内存
-
直接访问内存,不用
read()
/write()
非常方便
-
资料:
unpv22e-ch13
-
查看:
-
man shm_overview
-
ls /dev/shm
- 内存映射文件
注意:共享内存大小 = 文件大小
- 共享内存区对象(非亲缘进程)
- 匿名内存映射(亲缘进程)
| 风格 | 方式 |
| — | — |
| BSD | MAP_ANON
+mmap()
|
| Systerm V | /dev/zero
+open()
|
-
头文件:
sys/mman.h
-
库:
librt.so
3.1 函数
POSIX 共享内存有几个函数。
| No. | 操作 | 函数 |
| — | — | — |
| 1 | 创建 | int shm_open(const char *name, int oflag, mode_t mode)
|
| 2 | 删除 | int shm_unlink(const char *name)
|
| 3 | 建立内存映射 | void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)
|
| 4 | 关闭内存映射 | int munmap(void *start,size_t length)
|
3.1.1 创建
int shm_open(const char *name, int oflag, mode_t mode)
- 参数
| No. | 参数 | 含义 |
| — | — | — |
| 1 | name
| posix IPC名字,格式为/somename
|
| 2 | oflag
| 标志 |
| 3 | mode
| 权限 |
- 标志
| No. | 标志 | 作用 |
| — | — | — |
| 1 | O_CREAT
| 没有该对象则创建 |
| 2 | O_EXCL
| 如果O_CREAT指定,但name不存在,就返回错误 |
| 3 | `O_NONBLOCK` | 以非阻塞方式打开消息队列 |
| 4 | O_RDONLY
| 只读 |
| 5 | O_RDWR
| 读写 |
| 6 | `O_WRONLY` | 只写 |
| 7 | O_TRUNC
| 若存在则截断 |
- 权限
| No. | 权限 | 作用 |
| — | — | — |
| 1 | S_IWUSR
| 用户/属主写 |
| 2 | S_IRUSR
| 用户/属主读 |
| 3 | S_IWGRP
| 组成员写 |
| 4 | S_IRGRP
| 组成员读 |
| 5 | S_IWOTH
| 其他用户写 |
| 6 | S_IROTH
| 其他用户读 |
- 返回值
| No. | 返回值 | 含义 |
| — | — | — |
| 1 | -1
| 出错 |
| 2 | 其他 | 共享内存描述符 |
- 示例
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = shm_open(argv[1],O_CREAT|O_RDWR,0644);
ftruncate(fd,atoi(argv[2]));
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
}
3.1.2 删除
int shm_unlink(const char *name)
- 参数
| No. | 参数 | 含义 |
| — | — | — |
| 1 | name
| posix IPC名字 |
- 返回值
| No. | 返回值 | 含义 |
| — | — | — |
| 1 | -1
| 出错 |
| 2 | 0
| 成功 |
- 示例
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
shm_unlink(argv[1]);
}
3.1.3 建立内存映射
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)
- 参数
| No. | 参数 | 含义 |
| — | — | — |
| 1 | start
| 映射区的开始地址,通常使用NULL
,让系统决定映射区的起始地址 |
| 2 | length
| 映射区的长度,单位字节,不足一内存页按一内存页处理 |
| 3 | prot
| 内存保护标志 |
| 4 | flags
| 映射对象的类型 |
| 5 | fd
| 文件描述符,不能是套接字和终端的fd
,-1
为匿名内存映射 |
| 6 | offset
| 被映射对象内容的起点,只能是页大小的整数倍 |
如何获取页大小?
sysconf(_SC_PAGESIZE)
- 内存保护标志
| No. | 参数 | 含义 |
| — | — | — |
| 1 | PROT_EXEC
| 页内容可以被执行 |
| 2 | PROT_READ
| 页内容可以被读取 |
| 3 | PROT_WRITE
| 页可以被写入 |
| 4 | PROT_NONE
| 页不可访问,不能与文件的打开模式冲突 |
- 映射对象的类型
| No. | 参数 | 含义 |
| — | — | — |
| 1 | MAP_SHARED
| 变动共享 |
| 2 | MAP_PRIVATE
| 变动私有 |
| 3 | MAP_ANON
| 匿名内存映射 |
- 返回值
| No. | 返回值 | 含义 |
| — | — | — |
| 1 | MAP_FAILED
| 失败 |
| 2 | 非MAP_FAILED
| 共享内存地址 |
- 示例
写
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = shm_open(argv[1],O_RDWR,0);
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
strcpy(buf,argv[2]);
munmap(buf,BUFSIZ);
}
读
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = shm_open(argv[1],O_RDONLY,0);
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
printf(“%s\n”,buf);
munmap(buf,BUFSIZ);
}
3.1.4 关闭内存映射
int munmap(void *start,size_t length)
- 参数
| No. | 参数 | 含义 |
| — | — | — |
| 1 | start
| 映射内存起始地址 |
| 2 | length
| 内存大小 |
- 返回值
| No. | 返回值 | 含义 |
| — | — | — |
| 1 | 0
| 成功 |
| 2 | -1
| 失败 |
- 注意
关闭mmap
中的文件描述符不能删除内存映射。
4.1 内存映射文件
- 创建文件并且写入数据
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd = open(“./mmap.txt”,O_CREAT|O_RDWR,0644);
char str[] = “hello mmap\n”;
ftruncate(fd,sizeof(str));
void* buf = NULL;
if(( buf = mmap(NULL,sizeof(str),PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
strcpy(buf,str);
munmap(buf,sizeof(str));
close(fd);
}
问题
如果没有ftruncate(fd,sizeof(str));
会出现什么情况?
- 读取数据并且重新写入数据
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = open(“./mmap.txt”,O_RDWR);
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
printf(“%s\n”,buf);
strcpy(buf,“this sdfdsfdsfdsfdsfdsfdsfdsfdsfdsf\n”);
munmap(buf,BUFSIZ);
close(fd);
}
- 亲缘进程读写数据
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = open(“./mmap.txt”,O_CREAT|O_RDWR,0644);
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
if(fork()){
strcpy(buf,argv[1]);
}else{
printf(“%s\n”,buf);
}
munmap(buf,BUFSIZ);
close(fd);
}
- 非亲缘进程读写数据
写
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
int fd = open(“./mmap.txt”,O_CREAT|O_RDWR,0644);
void* buf = NULL;
if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED){
perror(“mmap error\n”);
return 1;
}
strcpy(buf,argv[1]);
munmap(buf,BUFSIZ);
close(fd);
}
读