一、内存映射
使一个
磁盘文件
与内存中
的一个缓冲区
相映射,进程可以像访问普通内存
一样对文件进行访问,不必再调用read,write
。
优点:
实现了用户空间
和内核空间
的高效交互方式
二、内存映射使用
void *mmap(void *addr, size_t length, int prot,
int flags, int fd, off_t offset);
创建共享内存映射
成功
返回创建的映射区首地址
,失败
返回MAP_FAILED( ((void *) -1)
),设置errno
值
addr
:指定要映射的内存地址
,一般设置为NULL
让操作系统自动选择合适的内存地址。length
:必须>0
。映射地址空间的字节数,它从被映射文件开头offset
个字节开始算起。prot
:指定共享内存的访问权限
。可取如下几个值的可选:PROT_READ
(可读),PROT_WRITE
(可写),PROT_EXEC
(可执行),PROT_NONE
(不可访问)。flags
:由以下几个常值指定:MAP_SHARED
(共享的)MAP_PRIVATE
(私有的),MAP_FIXED
(表示必须使用 start 参数作为开始地址,如果失败不进行修正),其中,MAP_SHARED
,MAP_PRIVATE
必选其一,而MAP_FIXED
则不推荐使用。MAP_ANONYMOUS
(匿名映射,用于血缘关系
进程间通信)fd
:表示要映射的文件句柄
。如果匿名映射
写-1
。offset
:表示映射文件的偏移量
,一般设置为0
表示从文件头部开始映射
。
三、内存映射注意事项
- (1)
创建映射区
的过程中,隐含
着一次对映射文件
的读操作
,将文件内容读取
到映射区
。 - (2) 当
MAP_SHARED
时,要求:映射区的权限
应<=
文件打开的权限
(出于对映射区的保护),如果不满足报非法参数(Invalid argument
)错误。
当MAP_PRIVATE
时候,mmap
中的权限
是对内存的限制
,只需要文件有读权限
即可,操作只在内存有效
,不会写到物理磁盘
,且不能
在进程间共享
。 - (3) 映射区的
释放
与文件关闭
无关,只要映射建立成功,文件可以立即关闭。 - (4) 用于映射的文件大小必须
>0
,当映射文件大小为0
时,指定非0大小创建映射区,访问映射地址会报总线错误
,指定0大小创建映射区
,报非法参数错误
(Invalid argument) - (5)
文件偏移量
必须为0
或者4K
的整数倍
(不是会报非法参数Invalid argument错误). - (6)
映射大小
可以大于文件大小
,但只能访问文件page的内存地址
,否则报总线错误
,超出映射的内存大小报段错误
.
- (7)
mmap
创建映射区出错概率非常高
,一定要检查返回值,确保映射区建立成功再进行后续操作。
四、释放映射
int munmap(void *addr, size_t length);
返回值:
成功
返回0
,失
败返回-1
,并设置errno
值。
addr
:调用mmap函数成功返回的映射区首地址
length
:映射区大小(即:mmap
函数的第二个参数)
示例:
写共享文件:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main(int argc, const char *argv[])
{
int len,fd = open("1.txt", O_RDWR);
void *map;
if(fd == -1)
{
perror("open:");
return -1;
}
len = lseek(fd, 0, SEEK_END);
map = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map == MAP_FAILED)
{
perror("mmap:");
}
while(1)
{
memcpy(map++, "a", 1);
sleep(1);
}
return 0;
}
读共享文件:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main(int argc, const char *argv[])
{
int len,fd = open("1.txt", O_RDWR);
void *map;
if(fd == -1)
{
perror("open:");
return -1;
}
len = lseek(fd, 0, SEEK_END);
map = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map == MAP_FAILED)
{
perror("mmap:");
}
while(1)
{
printf("%s\n", (char *)map);
sleep(1);
}
return 0;
}
五、System V共享内存
IPC 的key
ftok函数
key_t ftok(const char *path, int id);
- ·参数
path
是指定的文件名,这个文件必须是存在的而且可以访问
的。 id
是子序号
,它是一个8bit
的整数。即范围是0~255
。- 当函数执行成功,则会返回
key_t键值
,否则返回-1
。 - 在一般的
UNIX
中,通常是将文件的索引节点取出,然后在前面加上子序号就得到key_t
的值。
创建步骤
system V 共享内存使用步骤:
- ①
创建/打开
共享内存 - ②
映射共享内存
,即把指定的共享内存映射到进程的地址空间用于访问 - ③
读写
共享内存 - ④
撤销
共享内存映射 - ⑤
删除
共享内存对象
共享内存创建
int shmget(key_t key, int size, int shmflg);
共享内存映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
第二个参数一般写NULL,表示自动映射
第三参数一般写0 ,表示可读写
共享内存撤销
int shmdt(void *shmaddr);
撤销后,内存地址不可再访问。
共享内存控制
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid, IPC_RMID, NULL);删除共享内存
示例:
写:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<string.h>
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
void *buf;
if((key = ftok("keytest", 100) == -1))
{
perror("ftok:");
exit(-1);
}
printf("%d\n", key);
if((shmid = shmget(key, 512, IPC_CREAT|0666)) == -1)
{
perror("shmget:");
exit(-1);
}
printf("%d\n",shmid);
buf = shmat(shmid, NULL, 0);
strcpy(buf, "hello world");
while(1)
{
sleep(1);
}
return 0;
}
读:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/shm.h>
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
void *buf;
if((key = ftok("keytest", 100) == -1))
{
perror("ftok:");
exit(-1);
}
printf("%d\n",key);
buf = shmat(32822, NULL, 0);
printf("%s\n", (char *)buf);
shmdt(buf);
shmctl(32822, IPC_RMID, NULL);
return 0;
}