《Linux内核深度解析》第三章 内存学习笔记1
[复制链接]
内存学习之内存映射学习笔记
内存管理架构可以分为:用户空间、内核空间及硬件部分3个层面:
用户空间:应用程序使用malloc()申请内存资源/free()释放内存资源。
内核空间:内核总是驻留在内存中,是操作系统的一部分。内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。
硬件:处理器包含一个内存管理单元Memory Management Uint,MMU的部件,负责把虚拟地址转换为物理地址。内存管理单元包含一个页表缓存的部件,保存最近使用过的页表映射,避免每次把虚拟地址转换成物理地址都需要查询内存中的页表。
Linux内核的核心功能:
不管是用户空间还是内核空间,使用的地址都是虚拟地址,当需进程要实际访问内存的时候,会由内核的「请求分页机制」产生「缺页异常」调入物理内存页
程序只能通过虚拟地址访问外设寄存器,内核提供了一下函数来把外设寄存器的物理地址映射到虚拟地址空间
(1)ioremap()把外设寄存器的物理地址映射到内核虚拟地址空间
(2)io_remap_pfn_range()把外设寄存器的物理地址映射到进程的用户虚拟地址空间
内存映射是在进程的虚拟地址空间中创建一个映射,分为2种
- 文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件
- 匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源
也可以说内存映射就是磁盘文件的数据映射到内存,用户可以通过修改内存就能修改磁盘文件。简单来说就是将同一个文件存储映射到不同的进程中,两个进程通过改变文件内容(读写内存)来实现通信,不必在使用read和write函数等系统调用,加快文件的读取和写入。
原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
参数:
1.addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
2.length: 将文件的多大长度映射到内存.
3.prot: 映射区的保护方式, 可以是:
PROT_EXEC: 映射区可被执行.
PROT_READ: 映射区可被读取.
PROT_WRITE: 映射区可被写入.
PROT_NONE: 映射区不能存取.
4.flags: 映射区的特性, 可以是:
MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
fd: 由open返回的文件描述符, 代表要映射的文件.
offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.
测试例程
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
int main(int argc, const char* argv[]) {
int fd = -1;
int ret = -1;
void* addr = NULL;
// 1.以读写的方式打开txt文件
fd = open("txt", O_RDWR);
if (-1 == fd) {
perror("open");
return 1;
}
// 2.将文件映射到进程的虚拟地址空间
addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("映射成功.\n");
// 3.关闭文件
close(fd);
// 4.写文件
memcpy(addr, "123456", 6);
// 5.断开存储映射
munmap(addr, 1024);
return 0;
}
执行结果
|