1.内存映射(mmap)
我们在单片机中首先接触到了映射的概念
将一个寄存器的地址映射到了另外的一个存储空间中
内存映射:
内存映射(Memory Mapping)是一种在计算机科学中使用的技术,它允许将文件或其他设备的内容映射到进程的地址空间中,使得进程可以像访问内存一样访问这些内容。内存映射可以提供一种方便的方式来处理大文件或设备的数据,同时也可以加速数据的读取和写入操作。
在内存映射中,操作系统会将文件的内容映射到进程的虚拟内存地址空间中的一个或多个页面(page),从而使得进程可以通过读写内存地址来访问文件的内容,而无需使用传统的文件I/O操作。
使用内存映射的优点包括:
- 简化文件或设备的访问:通过内存映射,文件或设备的内容可以直接映射到内存中,使得进程可以像访问内存一样访问文件或设备的内容,而无需手动进行文件I/O操作(read write底层(硬件层次,操作相关的设备))。
- 提高性能:由于内存映射是通过文件系统缓存实现的,读取文件时可以直接从内存中获取数据,而避免了磁盘I/O操作,因此可以提高读取性能。
- 易于共享数据:多个进程可以将同一个文件映射到各自的地址空间中,从而实现共享数据。
需要注意的是,使用内存映射时需要谨慎处理内存访问越界和同步的问题,以避免出现内存错误或数据不一致的情况。此外,内存映射对于处理大文件或设备的数据非常有用,但在某些情况下可能不适用于小文件或频繁更新的数据。
直接采用共享内存的方式进行数据传输,可以不经过内核态,读写速度块。
映射对应关系:
内存映射部分相关函数:
建立映射区:mmap()函数
头文件:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
参数:
addr 地址, 填 NULL(映射区域的起始地址)--让系统帮你确定地址
length 长度 要申请的映射区的长度(字节)
rt-thread中创建任务开辟的空间,按字的大小去开辟
prot 权限
PROT_READ 可读
PROT_WRITE 可写
flags 标志位
MAP_SHARED 共享的 -- 对映射区的修改会影响源文件
MAP_PRIVATE 私有的
fd 文件描述符 需要打开一个文件
offset 指定一个偏移位置 , 从该位置开始映射--0
返回值
成功 返回映射区的首地址
失败 返回 MAP_FAILED ((void *) -1)
释放映射区:
int munmap(void *addr, size_t length);
addr 映射区的首地址
length 映射区的长度
扩展文件大小:
int truncate(const char *path, off_t length);
path 要拓展的文件
length 要拓展的长度
释放映射区域:
int munmap(void *addr, size_t length);
addr 映射区的首地址
length 映射区的长度
返回值
成功 返回 0
失败 返回 -1
1.磁盘空间---open函数新建一个文件
2.确定映射关系--mmap
3.确定文件大小--truncate
4.对映射区域进行写操作 ----
buf[128]="qqqdfsadfa......."
读操作:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//进程间的通信--队列中使用的头文件
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/mman.h>
int main(void)
{
//char buf[256];
int fd= open("map_file",O_RDWR|O_CREAT,0777);
printf("fd-->%d\n",fd);
// //int truncate(const char *path, off_t length);
// truncate("map_file",64);
//void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
char* buf=(char *) mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(buf==((char *)-1))
{
perror("mmap:");
exit(-1);
}
printf("buff-->%s\n",buf);
// strcpy(buf,"hello world");
return 0;
}
写操作:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//进程间的通信--队列中使用的头文件
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/mman.h>
int main(void)
{
// char buf[256];
int fd= open("map_file",O_RDWR|O_CREAT,0777);
printf("fd-->%d\n",fd);
//void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
char *buf=(char *) mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(buf==(char *)-1)
{
perror("mmap:");
exit(-1);
}
//int truncate(const char *path, off_t length);
truncate("map_file",8);
strcpy(buf,"hello world");
return 0;
}
2.共享内存
共享内存允许两个或者多个进程共享给定的存储区域。
共享内存和内存映射一样吗??
共享内存和内存映射都可以用于进程间的通信,但是内存映射主要是用于文件之间数据传输(相对于共享内存来说,速度更快)
内存映射相当于,在磁盘上开辟了空间,然后将需要用到的数据全部临时提取到内存中去运行处理,处理完成之后,后续在保存至物理磁盘中
共享内存是进程间的通信使用的最多的通信方式,用于传输数据量比较大的情景
共享内存是通过进程本身新建物理内存,然后,其他的进程只要是可以获取共享内存的描述符,即可访问存储空间
和物理存储相对应的还有虚拟内存
共享内存的特点
1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据, 共享这个内存区域的所有进程就可以立刻看到其中的内容。
2、 使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程正在向共享内存区写数据, 则在它做完这一步操作前, 别的进程不应当去读、 写这些数据。
互斥量:特殊信号量,只有0和1,代表对临界资源的访问,线程对于互斥资源的访问,是独占式享用,如果其他的线程需要访问互斥量,那么需要等待资源被释放,否则就访问不了。
互斥量可以有效地降低优先级反转带来的危害,如何解决的???
共享内存中没有互斥和同步的机制,需要结合信号量来使用
在 ubuntu 部分版本中共享内存限制值如下 共享存储区的最小字节数:
1 共享存储区的最大字节数: 32M
共享存储区的最大个数: 4096
每个进程最多能映射的共享存储区的个数: 4096
相关API函数:
获取标识符--shmget()
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:创建或打开一块共享内存区
参数:
key: IPC 键值
size: 该共享存储段的长度(字节)
shmflg: 标识函数的行为及共享内存的权限。
参数:
shmflg:
IPC_CREAT: 如果不存在就创建
IPC_EXCL: 如果已经存在则返回失败
位或权限位: 共享内存位或权限位后可以设置共享内存的访问权限, 格式
和 open 函数的 mode_t 一样, 但可执行权限未使用。
返回值:
成功: 返回共享内存标识符。
失败: 返回-1。
查看共享内存:
使用 shell 命令操作共享内存
查看共享内存
ipcs -m --内存
ipcs -q --队列
删除共享内存
ipcrm -m shmid --删除shmid的共享内存
ipcs -lm 查看当前系统的共享内存空间大小
共享区映射--shmat()
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr,int shmflg);
功能:
将一个共享内存段映射到调用进程的数据段中。
参数:
shmid: 共享内存标识符。
shmaddr: 共享内存映射地址(若为 NULL 则由系 统自动指
定), 推荐使用 NULL。
shmflg: 共享内存段的访问权限和映射条件
0: 共享内存具有可读可写权限。
SHM_RDONLY: 只读。
SHM_RND: (shmaddr 非空时才有效)
没有指定 SHM_RND 则此段连接到 shmaddr 所指定的地址上(shmaddr 必需
页对齐)。
指定了 SHM_RND 则此段连接到 shmaddr- shmaddr%SHMLBA 所表示的地址
上。
返回值:
成功: 返回共享内存段映射地址
失败: 返回 -1
注意:shmat 函数使用的时候第二个和第三个参数一般设为 NULL 和 0,
即系统自动指定共享内存地址, 并且共享内存可读可写。
解除共享映射区:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
shmaddr: 共享内存映射地址。
返回值:
成功返回 0, 失败-1
共享内存的控制:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
功能: 共享内存空间的控制。
参数:
shmid: 共享内存标识符。
cmd: 函数功能的控制。
buf: shmid_ds 数据类型的地址, 用来存放或修改共享内存的属性。
cmd: 函数功能的控制
IPC_RMID: 删除。
IPC_SET: 设置 shmid_ds 参数。
IPC_STAT: 保存 shmid_ds 参数。
SHM_LOCK: 锁定共享内存段(超级用户)。
SHM_UNLOCK: 解锁共享内存段。
返回值:
成功返回 0, 失败返回 -1。
注意: SHM_LOCK 用于锁定内存, 禁止内存交换。 并不代表共享内存被锁定后禁
止其它进程访问。 其真正的意义是: 被锁定的内存不允许被交换到虚拟内存中。这
样做的优势在于让共享内存一直处于内存中, 从而提高程序性能
读操作:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//进程间的通信--队列中使用的头文件
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
//按字节寻址
int main(void)
{
// // char buf[256];
// int fd= open("map_file",O_RDWR|O_CREAT,0777);
// printf("fd-->%d\n",fd);
// //void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
// char *buf=(char *) mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
// if(buf==(char *)-1)
// {
// perror("mmap:");
// exit(-1);
// }
// //int truncate(const char *path, off_t length);
// truncate("map_file",8);
// strcpy(buf,"hello world");
key_t key =ftok("./",3);
//获取共享内存的描述符
//int shmget(key_t key, size_t size,int shmflg);
int shm_id = shmget(key,32,IPC_CREAT|0666);
//获取共享内存的地址
// void *shmat(int shmid, const void *shmaddr,int shmflg);
char *buf=(char *)shmat(shm_id,NULL,0);
// strcpy(buf,"zz2302 hello");
// 读数据
printf("buf-->%s\n",buf);
memset(buf,0,32);
//int shmdt(const void *shmaddr);
return 0;
}
写操作:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//进程间的通信--队列中使用的头文件
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
//按字节寻址
int main(void)
{
// // char buf[256];
// int fd= open("map_file",O_RDWR|O_CREAT,0777);
// printf("fd-->%d\n",fd);
// //void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
// char *buf=(char *) mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
// if(buf==(char *)-1)
// {
// perror("mmap:");
// exit(-1);
// }
// //int truncate(const char *path, off_t length);
// truncate("map_file",8);
// strcpy(buf,"hello world");
key_t key =ftok("./",3);
//获取共享内存的描述符
//int shmget(key_t key, size_t size,int shmflg);
int shm_id = shmget(key,32,IPC_CREAT|0666);
//获取共享内存的地址
// void *shmat(int shmid, const void *shmaddr,int shmflg);
char *buf=(char *)shmat(shm_id,NULL,0);
strcpy(buf,"zz2302 hello");
//int shmdt(const void *shmaddr);
return 0;
}
进程间的通信方式:
1.信号
2.管道
3.消息队列
4.共享内存