内存学习(四):内存映射1

简介: 内存学习(四):内存映射1

内存映射

一、内存映射概览

1、内存映射

内存映射是在进程的虚拟地址空间中创建一个映射,分为以下两种。

  • (1)文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。
  • (2)匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源。

通常把文件映射的物理页称为文件页把匿名映射的物理页称为匿名页。

2、共享映射

根据修改是否对其他进程可见和是否传递到底层文件,内存映射分为共享映射和私有映射。

  • (1)共享映射:修改数据时映射相同区域的其他进程可以看见,如果是文件支持的映射,修改会传递到底层文件。
  • (2)私有映射:第一次修改数据时会从数据源复制一个副本,然后修改副本,其他进程看不见,不影响数据源。

两个进程可以使用共享的文件映射实现共享内存。匿名映射通常是私有映射共享的匿名映射只可能出现在父进程和子进程之间

在进程的虚拟地址空间中,代码段和数据段是私有的文件映射未初始化数据段、堆和栈是私有的匿名映射。

3、映射原理

  • (1)创建内存映射的时候,在进程的用户虚拟地址空间中分配一个虚拟内存区域。
  • (2)Linux内核采用延迟分配物理内存的策略,在进程第一次访问虚拟页的时候,产生缺页异常。如果是文件映射,那么分配物理页,把文件指定区间的数据读到物理页中,然后在页表中把虚拟页映射到物理页;如果是匿名映射,那么分配物理页,然后在页表中把虚拟页映射到物理页。(把数据放进去,然后在表上登记好。)

二、应用编程接口

内存管理子系统提供了以下常用的系统调用。

1-常用的系统调用

  • (1)mmap()用来创建内存映射
void *mmap(void *addr, size_t length, int prot, int flags,
                int fd, off_t offset);
  • (2)mremap()用来扩大或缩小已经存在的内存映射,可能同时移动。
void *mremap(void *old_address, size_t old_size,
                  size_t new_size, int flags, ... /* void *new_address */);
  • (3)munmap()用来删除内存映射。
int munmap(void *addr, size_t length);
  • (4)brk()用来设置堆的上界
int brk(void *addr);
  • (5)remap_file_pages()用来创建非线性的文件映射,即文件区间和虚拟地址空间之间的映射不是线性关系,现在被废弃了。

         
  • (6)mprotect()用来设置虚拟内存区域的访问权限。
int mprotect(void *addr, size_t len, int prot);
  • (7)madvise()用来向内核提出内存使用的建议,应用程序告诉内核期望怎样使用指定的虚拟内存区域,以便内核可以选择合适的预读和缓存技术。
int madvise(void *addr, size_t length, int advice);

2-内核空间中可以使用两个函数

  • (1)remap_pfn_range把内存的物理页映射到进程的虚拟地址空间,这个函数的用处是实现进程和内核共享内存
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn,
    unsigned long size, pgprot_t prot);
  • (2)io_remap_pfn_range把外设寄存器的物理地址映射到进程的虚拟地址空间,进程可以直接访问外设寄存器。
int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long
    pfn, unsigned long size, pgprot_t prot);

应用程序通常使用C标准库提供的函数malloc()申请内存。

glibc库的内存分配器ptmalloc使用brk或mmap向内核以页为单位申请虚拟内存,然后把页划分成小内存块分配给应用程序。

默认的阈值是128KB,如果应用程序申请的内存长度小于阈值ptmalloc分配器使用brk向内核申请虚拟内存否则ptmalloc分配器使用mmap向内核申请虚拟内存

应用程序可以直接使用mmap向内核申请虚拟内存。

下面来讲讲

3-系统调用mmap()

系统调用mmap()有以下用处。

  • (1)进程创建匿名的内存映射,把内存的物理页映射到进程的虚拟地址空间
  • (2)进程把文件映射到进程的虚拟地址空间,可以像访问内存一样访问文件,不需要调用系统调用read()和write()访问文件,从而避免用户模式和内核模式之间的切换,提高读写文件的速度
  • (3)两个进程针对同一个文件创建共享的内存映射,实现共享内存

函数原型:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数如下:

  • (1)addr:起始虚拟地址。如果addr是0,内核选择虚拟地址。如果addr不是0,内核把这个参数作为提示,在附近选择虚拟地址。
  • (2)length:映射的长度,单位是字节。
  • (3)prot:保护位。
PROT_EXEC:页可执行。
    PROT_READ:页可读。
    PROT_WRITE:页可写。
    PROT_NONE:页不可访问。
  • (4)flags:标志。常用的标志如下。
MAP_SHARED:共享映射。
    MAP_PRIVATE:私有映射。
    MAP_ANONYMOUS:匿名映射。
    MAP_FIXED:固定映射,不要把参数addr解释为一个提示,映射的起始地址必须是参数addr,必须是页长度的整数倍。
    MAP_HUGETLB:使用巨型页。
    MAP_LOCKED:把页锁在内存中。
    MAP_NORESERVE:不预留物理内存。
    MAP_NONBLOCK:不阻塞,和MAP_POPULATE联合使用才有意义,从Linux 2.6.23开始,该标志导致MAP_POPULATE什么都不做。
    MAP_POPULATE:填充页表,即分配并且映射到物理页。如果是文件映射,该标志导致预读文件。
  • (5)fd:文件描述符。仅当创建文件映射的时候,这个参数才有意义。如果是匿名映射,有些实现要求参数fd是−1,可移植的应用程序应该保证参数fd是−1。
  • (6)offset:偏移,单位是字节,必须是页长度的整数倍。仅当创建文件映射的时候,这个参数才有意义。

返回值:如果成功,返回起始虚拟地址,否则返回负的错误号。

4-系统调用mprotect()

mprotect()用来设置虚拟内存区域的访问权限

函数原型:

int mprotect(void *addr, size_t len, int prot);

参数如下。

  • (1)addr:起始虚拟地址,必须是页长度的整数倍。
  • (2)len:虚拟内存区域的长度,单位是字节。
  • (3)prot:保护位。
  • PROT_EXEC:页可执行。
  • PROT_READ:页可读。
  • PROT_WRITE:页可写。
  • PROT_NONE:页不可访问。

返回值:如果成功,返回0,否则返回负的错误号。

5-系统调用madvise()

madvise()用来向内核提出内存使用的建议,应用程序告诉内核期望怎样使用指定的虚拟内存区域,以便内核可以选择合适的预读和缓存技术。

函数原型:

int madvise(void *addr, size_t length, int advice);

参数如下。

  • (1)addr:起始虚拟地址,必须是页长度的整数倍。
  • (2)length:虚拟内存区域的长度,单位是字节。
  • (3)advice:建议。POSIX标准定义的建议值如下。
  • MADV_NORMAL:不需要特殊处理,这是默认值。
  • MADV_RANDOM:预期随机访问指定范围的页,预读的用处比较小。
  • MADV_SEQUENTIAL:预期按照顺序访问指定范围的页,所以可以激进地预读指定范围的页,并且进程在访问页以后很快释放。
  • MADV_WILLNEED:预期很快就会访问指定范围的页,所以可以预读指定范围的页。
  • MADV_DONTNEED:预期近期不会访问指定范围的页,即进程已经处理完指定范围的页,内核可以释放相关的资源。
    Linux私有的建议值如下。
  • MADV_REMOVE:进程想要释放指定范围的页和相关的后备存储。
  • MADV_DONTFORK:在执行fork()的时候从子进程的地址空间删掉指定范围的页。
  • MADV_DOFORK:取消MADV_DONTFORK,在执行fork()的时候不从子进程的地址空间删掉指定范围的页。
  • MADV_HWPOISON:毒化指定范围的页,像内存损坏一样处理对指定范围的页的访问。
  • MADV_MERGEABLE:允许KSM(Kernel Samepage Merging,内核相同页合并)合并数据相同的页。
  • MADV_UNMERGEABLE:取消MADV_MERGEABLE,不允许合并数据相同的页。
  • MADV_SOFT_OFFLINE:使指定范围的页软下线,即内存页被保留,但是下一次访问的时候,把数据复制到新的物理页,旧的物理页下线,对进程不可见。这个特性用来测试处理内存错误的代码。
  • MADV_HUGEPAGE:允许指定范围使用透明巨型页。
  • MADV_NOHUGEPAGE:不允许指定范围使用透明巨型页。
  • MADV_DONTDUMP:生成核心转储文件的时候不要包含指定范围的页。
  • MADV_DODUMP:取消MADV_DONTDUMP,生成核心转储文件的时候包含指定范围的页。
  • MADV_FREE:从4.5版本开始支持,进程不再需要指定范围的页,内核可以释放这些页,释放可以延迟到内存不足的时候。

返回值:如果成功,返回0,否则返回负的错误号。

内容来自前辈书籍:《Linux内核深度解析》

目录
相关文章
|
2月前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
55 6
|
3月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
110 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
7月前
|
NoSQL Java Redis
Redis系列学习文章分享---第十八篇(Redis原理篇--网络模型,通讯协议,内存回收)
Redis系列学习文章分享---第十八篇(Redis原理篇--网络模型,通讯协议,内存回收)
91 0
|
7月前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
58 0
|
3月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
66 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
4月前
|
存储 缓存 Linux
用户态内存映射
【9月更文挑战第20天】内存映射不仅包括物理与虚拟内存间的映射,还涉及将文件内容映射至虚拟内存,使得访问内存即可获取文件数据。mmap 系统调用支持将文件或匿名内存映射到进程的虚拟内存空间,通过多级页表机制实现高效地址转换,并利用 TLB 加速映射过程。TLB 作为页表缓存,存储频繁访问的页表项,显著提升了地址转换速度。
|
3月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
69 0
|
4月前
|
存储 安全 Linux
将文件映射到内存,像数组一样访问
将文件映射到内存,像数组一样访问
43 0
|
5月前
|
存储 JavaScript 前端开发
学习JavaScript 内存机制
【8月更文挑战第23天】学习JavaScript 内存机制
44 3
|
4月前
|
消息中间件 Linux 容器
共享内存的创建和映射过程
【9月更文挑战第1天】消息队列、共享内存及信号量在使用前需生成key并获取唯一ID,均通过`xxxget`函数实现。

热门文章

最新文章