操作系统笔记之内存映射

操作系统笔记之内存映射

—— 杭州 2024-02-04

在这里插入图片描述

code review!

文章目录

  • 操作系统笔记之内存映射
    • 一.内存映射概念
      • 1. 文件映射到内存 (Memory-Mapped Files)
      • 2. 虚拟内存管理 (Virtual Memory Management)
      • 3. 内存映射I/O (Memory-Mapped I/O)
      • 4. 图形处理 (Graphics Processing)
      • 5. 数据库内存映射 (Database Memory Mapping)
      • 6. 分布式内存映射 (Distributed Memory Mapping)
    • 二.虚拟内存管理 (Virtual Memory Management)
        • 1. 虚拟地址空间
        • 2. 物理地址空间
        • 3. 页
        • 4. 页表
        • 5. 分页机制
        • 6. TLB (Translation Lookaside Buffer)
        • 7. 页错误(Page Fault)
    • 三.文件映射到内存 (Memory-Mapped Files)
      • 内存映射的原理
      • 内存映射的优势
      • 内存映射的劣势
      • 如何使用内存映射
      • 传统的文件读写API:
      • 内存映射方式:
      • ChatGPT——mmap()详解
      • 基本用法
      • 示例

一.内存映射概念

内存映射(Memory Mapping)是一个较为广泛的概念,它可以在不同的计算机科学领域内有不同的应用和含义。以下是内存映射的几个常见用途:

1. 文件映射到内存 (Memory-Mapped Files)

这是内存映射最常见的用途之一,涉及将磁盘上的文件内容映射到进程的地址空间。这样,文件可以像访问内存一样被访问,而不需要显式地执行读写操作。这通常用于提高文件I/O操作的性能,因为它允许操作系统利用虚拟内存管理来进行优化。

2. 虚拟内存管理 (Virtual Memory Management)

在虚拟内存系统中,内存映射描述了虚拟地址空间到物理内存的转换过程。操作系统通过内存映射来确保每个进程有自己独立的地址空间,并将这些地址映射到物理RAM或者磁盘上的交换空间。

3. 内存映射I/O (Memory-Mapped I/O)

在硬件设计和低级编程中,内存映射I/O是指将设备寄存器的I/O地址映射到程序的地址空间,使得设备I/O可以使用普通的内存访问指令进行。这简化了对硬件设备的编程,因为它允许程序员使用对内存的操作来控制硬件设备。

4. 图形处理 (Graphics Processing)

在图形处理中,内存映射可以指显存(GPU内存)和系统内存之间的映射。这使得CPU可以访问和操作GPU内存中的数据,例如纹理和帧缓冲区。

5. 数据库内存映射 (Database Memory Mapping)

某些数据库系统使用内存映射技术来提高访问持久化数据的效率。通过将数据库文件映射到内存,数据库查询和操作可以更快地执行,因为它们直接在内存中进行。

6. 分布式内存映射 (Distributed Memory Mapping)

在分布式系统中,内存映射也可以指不同机器之间共享内存空间的技术。这允许构建高性能的分布式应用程序,使得远程数据可以像本地内存一样被访问和修改。

内存映射是一个多面的概念,它的具体含义依赖于上下文。在不同的系统和应用程序中,内存映射的机制和目的可能会有所不同。

二.虚拟内存管理 (Virtual Memory Management)

虚拟内存管理是操作系统中用于抽象物理内存的一种机制,它允许每个程序像拥有一台独立的、拥有足够内存的计算机一样运行,即使实际的物理内存可能不足以同时容纳所有运行的程序。

这里的"内存映射"指的是虚拟地址(由程序使用)到物理地址(RAM中的实际位置)的转换。操作系统通过维护一张映射表(页表)来管理这个转换过程。以下是虚拟内存管理的关键概念和组件:

1. 虚拟地址空间
  • 虚拟地址空间是指一个程序可见的内存范围,这个范围由操作系统管理,对程序来说就像是它独占的内存。
  • 每个程序都有自己的虚拟地址空间,它是连续的,从程序的角度看不会感知到物理内存的实际分布情况。
2. 物理地址空间
  • 物理地址空间是实际的RAM内存地址。
  • 物理内存通常是不连续分配给程序的,因为它被所有程序和操作系统共享。
3. 页
  • 虚拟内存和物理内存被分割成大小相同的块,称为"页"(在虚拟内存中)和"页帧"(在物理内存中)。
  • 页的大小可以根据系统设计而不同,常见的大小有4KB、2MB等。
4. 页表
  • 页表是操作系统维护的数据结构,用于跟踪虚拟页和物理页帧之间的映射关系。
  • 当程序访问其虚拟地址空间时,操作系统使用页表来找到对应的物理地址。
5. 分页机制
  • 分页机制是一种内存管理技术,它允许虚拟内存被分割为多个页,并且每个页可以单独映射到物理内存中的任意位置。
  • 这种技术也支持"懒加载"(按需加载)和"交换"(或"换出"),即将不活跃的页移动到磁盘上的交换空间,腾出物理内存给其他程序使用。
6. TLB (Translation Lookaside Buffer)
  • TLB是一种缓存,它存储最近使用的页表条目,以加快虚拟地址到物理地址的转换过程。
  • TLB命中(找到缓存的页表条目)速度快于完整的页表查找。
7. 页错误(Page Fault)
  • 当程序访问的虚拟地址没有映射到物理内存时,会发生页错误中断。
  • 操作系统必须处理这个中断,通常是通过将所需的数据从磁盘加载到内存中,并更新页表来解决。

通过虚拟内存管理,操作系统提供了一种有效的方式来利用有限的物理内存资源,并且为每个程序提供了一种看似拥有无限内存资源的环境。这大大简化了程序设计,并提高了系统的稳定性和安全性。

三.文件映射到内存 (Memory-Mapped Files)

内存映射(Memory Mapping)是一种文件I/O处理的方法,它将文件内容映射到进程的地址空间,使得程序可以像访问内存一样对文件进行读写,而不必使用传统的文件读写API。内存映射可以提高文件操作的性能,特别是对于大文件的处理。

内存映射的原理

当一个文件被内存映射后,操作系统会保留进程地址空间中的一段区域,这段区域的地址范围会与文件内容建立直接的映射关系。进程可以通过指针直接访问这段内存,操作系统负责将对应的文件数据加载到物理内存中,并在必要时同步到磁盘文件。

内存映射的具体实现依赖于操作系统的虚拟内存管理机制。当进程访问映射内存中某个位置的数据时,如果该数据尚未加载到物理内存,则会触发一个缺页中断(page fault),操作系统随后将文件对应部分的数据加载到内存中,并重新开始指令的执行。

内存映射的优势

  1. 性能提升:避免了传统文件I/O的系统调用开销和用户空间与内核空间之间的数据拷贝。
  2. 简化编程模型:程序员可以使用指针操作文件,无需调用read/write等文件操作函数。
  3. 便于共享:内存映射的文件可以被多个进程共享,便于实现进程间通信。
  4. 按需加载:文件数据按需加载到内存,而不是一次性全部加载,可以有效利用内存资源。
  5. 自动同步:操作系统会在适当的时候将修改后的内存数据同步回磁盘文件。

内存映射的劣势

  1. 内存消耗:映射大文件时,如果同时访问文件的多个部分,可能会消耗大量的物理内存。
  2. 文件大小限制:对于32位系统,内存映射的文件大小受到地址空间限制,通常不能超过2GB。
  3. 复杂的错误处理:内存映射文件在访问时可能会因为I/O错误导致程序异常终止。

如何使用内存映射

在Unix-like系统中,可以通过mmap()系统调用实现内存映射。以下是一个简单的示例:

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>int main() {// 打开文件int fd = open("example.txt", O_RDONLY);struct stat sb;if (fd == -1) {// 错误处理}// 获取文件的属性if (fstat(fd, &sb) == -1) {// 错误处理}// 执行内存映射char *mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);if (mapped == MAP_FAILED) {// 错误处理}// 现在可以通过mapped指针访问文件内容// 解除内存映射if (munmap(mapped, sb.st_size) == -1) {// 错误处理}close(fd);return 0;
}

在Windows操作系统中,内存映射可以通过CreateFileMapping和MapViewOfFile函数来实现。

内存映射在文件I/O密集型应用中非常有用,如数据库、文件系统以及需要高速文件处理的应用程序。然而,在使用时需要考虑到内存映射的各种限制,并实现适当的错误处理机制。

内存映射文件的工作原理如下:

  1. 文件映射:操作系统提供的内存映射API(如Windows的 CreateFileMappingMapViewOfFile,或者Unix/Linux的 mmap 函数)用来创建文件的内存映射。这个映射过程会将文件内容关联到进程的虚拟地址空间。

  2. 内存访问:一旦文件被映射,它就可以像普通的内存区域一样被访问。你可以通过指针来读写这块内存,操作系统会自动将这些读写操作转换为对文件的读写。

  3. 数据同步:对映射内存的更改可能会被延迟写入到实际的文件中,这取决于具体的同步策略。在某些情况下,开发者可能需要显式地告诉操作系统将更改立即写回文件(例如,使用 msync 函数)。

  4. 映射解除:完成对文件的操作后,应该解除文件的内存映射,释放资源。在Unix/Linux系统中,这通常通过 munmap 函数实现,在Windows中通过 UnmapViewOfFileCloseHandle 函数实现。

使用内存映射来读写文件的优势包括:

  • 性能提升:对于大文件操作,内存映射可以提高性能,因为它避免了传统的文件I/O操作中的系统调用和缓冲区管理开销。
  • 简化编码:开发者可以直接通过指针来读写文件数据,这种方式比传统的文件I/O API更直观简单。
  • 便于文件共享:映射文件可以被多个进程共享,为进程间通信提供了一种方便的机制。

需要注意的是,内存映射文件也有其局限性和风险:

  • 内存消耗:映射大文件到内存会消耗等量的虚拟地址空间,对于32位系统来说,这可能是个问题。
  • 文件大小变化:如果映射的文件在映射期间被外部过程修改,并且文件大小发生变化,可能会导致访问违规。
  • 数据一致性:确保内存中的更改及时同步到磁盘上,以避免数据丢失。

总之,内存映射文件提供了一种高效和便捷的文件访问方式,特别是对于需要频繁读写大型文件的应用程序。但同时,开发者需要理解其工作原理和潜在的风险。

让我们通过一个简单的例子来比较传统的文件读写API和内存映射方式。假设我们有一个文本文件,我们想要读取内容并修改其中的一些数据。

传统的文件读写API:

在C语言中,你可能会使用标准的文件I/O函数如 fopen, fread, fwrite, 和 fclose。以下是一个简单的例子,展示了如何使用这些API来读取和修改文件内容:

#include <stdio.h>
#include <stdlib.h>int main() {FILE *file;char buffer[1024];// 打开文件file = fopen("example.txt", "r+");if (file == NULL) {perror("Error opening file");return -1;}// 读取文件内容到缓冲区size_t bytes_read = fread(buffer, sizeof(char), sizeof(buffer), file);if (bytes_read == 0 && ferror(file)) {perror("Error reading file");fclose(file);return -1;}// 修改缓冲区中的数据buffer[0] = 'H'; // 仅示例:修改文件的第一个字符// 回到文件开始fseek(file, 0, SEEK_SET);// 将修改后的缓冲区写回文件if (fwrite(buffer, sizeof(char), bytes_read, file) != bytes_read) {perror("Error writing to file");fclose(file);return -1;}// 关闭文件fclose(file);return 0;
}

内存映射方式:

使用内存映射文件,你可以避免使用标准的文件I/O函数,而是通过映射文件到内存,直接像操作内存一样操作文件。以下是使用 mmap 在Linux上的一个示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd;struct stat sb;char *mapped;// 打开文件fd = open("example.txt", O_RDWR);if (fd == -1) {perror("Error opening file for writing");return -1;}// 获取文件的属性if (fstat(fd, &sb) == -1) {perror("Error getting the file size");close(fd);return -1;}// 执行映射mapped = mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("Error mapping the file");close(fd);return -1;}// 现在文件的内容就在mapped指针中// 修改映射区的数据,将反映到文件中mapped[0] = 'H'; // 仅示例:修改文件的第一个字符// 解除映射if (munmap(mapped, sb.st_size) == -1) {perror("Error un-mapping the file");}// 关闭文件close(fd);return 0;
}

在这个例子中,通过内存映射,我们可以直接通过 mapped 指针来访问和修改文件内容,就像它是程序的一部分内存一样。这种方法避免了使用缓冲区,也没有显式的读写操作。文件的读写操作是通过对内存的操作隐式完成的。

总结:

  • 传统方法:需要显式地读取数据到内存中的缓冲区,处理完后再显式写回文件。
  • 内存映射方法:通过将文件直接映射到进程地址空间,可以像操作内存一样操作文件,无需显式的读写调用。

ChatGPT——mmap()详解

mmap() 是一个 Unix/Linux 系统调用,它提供了一种将文件或其他对象映射到内存的方法。这允许程序员直接在内存中访问文件内容,就像它是一个数组一样,这样可以提高文件的访问速度,因为避免了传统的读写系统调用的开销。mmap() 通常用于文件的读取、写入,以及创建共享内存。

基本用法

这是 mmap() 函数的基本原型:

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

参数说明:

  • addr: 指定映射区域的起始地址。通常传递 NULL 让系统自己选择地址。
  • length: 映射区域的长度。单位是字节。
  • prot: 指定映射区域的保护。可以是以下几个值的组合:
    • PROT_READ: 映射区域可被读取。
    • PROT_WRITE: 映射区域可被写入。
    • PROT_EXEC: 映射区域可执行代码。
    • PROT_NONE: 映射区域不能访问。
  • flags: 控制映射区域的特性。主要有以下几个选项:
    • MAP_SHARED: 对映射区域的修改会反映到被映射的文件上,并且对其他映射了同一个文件的进程可见。
    • MAP_PRIVATE: 对映射区域的修改不会影响原文件,而是创建一个写时复制的私有副本。
    • MAP_ANONYMOUS: 映射区域不是映射到具体的文件上,通常用于共享内存的创建。
  • fd: 被映射文件的文件描述符。对于匿名映射,该值通常设为 -1
  • offset: 文件映射的起始偏移量,必须是系统页面大小的整数倍。

返回值:成功时,mmap() 返回指向映射区域的指针。失败时,返回 MAP_FAILED(void *) -1),并设置 errno 以指示错误。

示例

这是一个使用 mmap() 映射文件并读取内容的简单示例:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {const char *filepath = "example.txt";int fd = open(filepath, O_RDONLY);if (fd == -1) {perror("Error opening file for reading");exit(EXIT_FAILURE);}struct stat sb;if (fstat(fd, &sb) == -1) {perror("Error getting the file size");exit(EXIT_FAILURE);}char *map = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);if (map == MAP_FAILED) {close(fd);perror("Error mmapping the file");exit(EXIT_FAILURE);}// Now the file content is in memory and can be accessed via `map` pointer.for (size_t i = 0; i < sb.st_size; i++) {printf("%c", map[i]);}// Unmap and close the file.if (munmap(map, sb.st_size) == -1) {perror("Error un-mmapping the file");}close(fd);return 0;
}

在上面的代码中,我们首先打开了一个文件,然后使用 fstat() 获取文件的大小,接着使用 mmap() 创建一个文件内容的内存映射。文件的内容现在可以通过指针 map 访问。在处理完文件内容后,我们使用 munmap() 删除映射,并关闭文件。

注意,使用 mmap() 时必须小心地管理内存和文件的描述符,避免内存泄漏或文件描述符泄漏等问题。此外,当使用 MAP_SHARED 标志时,对映射区域的改变可能会影响到原文件,这在多进程间共享数据时非常有用,但在不同步的情况下也可能导致数据不一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/454592.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站为什么要用CND?

CDN对于网站来说至关重要&#xff0c;CDN对网站的重要性主要体现在可以提升用户体验、提高网站安全性、减轻服务器负担、提高SEO排名等&#xff0c;还可以为网站节省带宽成本。因此&#xff0c;选择一个性能好、速度快的CDN是很有必要的。 CDN对于现代网站来说是不可或缺的&am…

MySQL数据库练习【一】

MySQL数据库练习【一】 一、建库建表-数据准备二、习题2.1. 查询部门编号为30的部门的员工详细信息2.2.查询从事clerk工作的员工的编号、姓名以及其部门号2.3.查询奖金多于基本工资的员工的信息、查询奖金小于基本工资的员工的信息2.4.查询奖金多于基本工资60%的员工的信息2.5.…

libev-ev_timer定时器的理解

1.相关说明 本文主要自己对于libev的ev_timer定时器的代码流程梳理&#xff0c;主要有ev_timer结构体定义变量的初始化&#xff0c;定时器变量的参数设置&#xff0c;定时器变量的使用 2.相关代码流程 下面是图片 3.相关实现代码 main.c #include <stdio.h> #include…

红队打靶练习:HEALTHCARE: 1

目录 信息收集 1、arp 2、nmap 3、nikto 4、whatweb 目录探测 1、gobuster 2、dirsearch WEB web信息收集 gobuster cms sqlmap 爆库 爆表 爆列 爆字段 FTP 提权 信息收集 本地提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Inte…

【前端web入门第四天】02 CSS三大特性+背景图

文章目录: 1. CSS三大特性 1.1继承性 1.2 层叠性 1.3 优先级 1.3.1 优先级1.3.2 优先级-叠加计算规则 2. 背景图 2.1 背景属性2.2 背景图2.3 背景图的平铺方式2.4 背景图位置2.5 背景图缩放2.6 背景图固定2.7 背景复合属性 1. CSS三大特性 1.1继承性 什么是继承性? 子级默…

点大商城V2版 2.5.5全插件开源独立版 百度+支付宝+QQ+头条+小程序端+unipp开源端安装测试教程

点大商城V2是一款采用全新界面设计支持多端覆盖的小程序应用&#xff0c;支持H5、微信公众号、微信小程序、头条小程序、支付宝小程序、百度小程序&#xff0c;本程序是点大商城V2独立版&#xff0c;包含全部插件&#xff0c;代码全开源&#xff0c;并且有VUE全端代码。分销&am…

Python学习路线 - Python高阶技巧 - PySpark案例实战

Python学习路线 - Python高阶技巧 - PySpark案例实战 前言介绍Spark是什么Python On SparkPySparkWhy PySpark 基础准备PySpark库的安装构建PySpark执行环境入口对象PySpark的编程模型 数据输入RDD对象Python数据容器转RDD对象读取文件转RDD对象 数据计算map方法flatMap方法red…

【Linux多线程】线程池

目录 线程池的概念 线程池的优点 线程池的应用场景 线程池示例 代码实现 线程池的概念 线程池是一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。 线程…

机器学习---半监督学习简单示例(标签传播算法)

1. 使用半监督学习方法 Label Spreading 在一个生成的二维数据集上进行标签传播 import numpy as np import matplotlib.pyplot as plt from sklearn.semi_supervised import label_propagation from sklearn.datasets import make_circles# generate ring with inner box n_s…

北斗卫星在物联网时代的应用探索

北斗卫星在物联网时代的应用探索 在当今数字化时代&#xff0c;物联网的应用已经深入到人们的生活中的方方面面&#xff0c;让我们的生活更加智能便捷。而北斗卫星系统作为我国自主研发的卫星导航系统&#xff0c;正为物联网的发展提供了强有力的支撑和保障。本文将全面介绍北…

前端复杂 table 渲染及 excel.js 导出

转载请注明出处&#xff0c;点击此处 查看更多精彩内容 现在我们有一个如图&#xff08;甚至更复杂&#xff09;的表格需要展示到页面上&#xff0c;并提供下载为 excel 文件的功能。 前端表格渲染我们一般会使用 element-ui 等组件库提供的 table 组件&#xff0c;这些组件一般…

企业飞书应用机器人,使用python自动发送文字内容到群消息

文章目录 创建企业应用与开通机器人飞书发送信息的工具函数 创建企业应用与开通机器人 需要先创建应用&#xff0c;然后进入应用后&#xff0c;点击添加应用能力创建机器人&#xff1a; 参考官方文档&#xff0c;获取两个参数&#xff1a;app_id与app_secret 官方说明文档&…