如何进行内存映射和文件映射操作?

内存映射和文件映射是在操作系统和程序之间共享数据的强大技术。它们可以用于提高文件的读写效率、内存管理和进程间通信。对于C语言初学者来说,了解如何进行内存映射和文件映射操作是一项有价值的技能。在本文中,我们将详细讨论内存映射和文件映射的概念、用途以及如何在C语言中进行操作。

什么是内存映射和文件映射?

内存映射(Memory Mapping)

内存映射是一种将文件或其他可存取的对象映射到程序的地址空间中的技术。这意味着文件的内容(或其他对象)被映射到程序的内存中,使得程序可以像访问内存一样访问文件的内容。内存映射将文件内容与内存缓冲区相关联,实现了对文件的高效访问。

内存映射通常与虚拟内存系统一起使用,操作系统会根据需要将文件的部分或全部内容加载到物理内存中,以便程序能够快速读取或写入数据。内存映射还允许多个进程共享相同的内存映射区域,这在一些特定的应用程序中非常有用。

文件映射(File Mapping)

文件映射是Windows操作系统中的概念,它类似于内存映射,但更加通用。文件映射允许将文件的内容映射到内存中,以便程序可以访问文件的内容,而不是通过传统的文件I/O操作。

文件映射不仅可以用于文件,还可以用于其他内核对象,如共享内存、管道等。在Windows中,文件映射通常通过CreateFileMappingMapViewOfFile等API函数来实现。

在本文中,我们将重点关注内存映射,因为它是跨平台的,并且在许多操作系统上都有相似的实现。

内存映射的用途

内存映射具有多种用途,下面列出了一些主要的应用场景:

1. 文件I/O优化

内存映射可以提高文件的读写效率。当文件被映射到内存中后,程序可以直接从内存中读取或写入数据,而不需要使用传统的文件I/O函数,如freadfwrite。这通常会导致更快的文件操作速度,尤其是对于大型文件。

2. 共享内存

内存映射允许多个进程共享相同的内存映射区域。这在多进程应用程序中非常有用,因为它允许进程之间共享数据,而不需要使用复杂的进程间通信(IPC)机制。

3. 内存映射数据库

一些数据库系统使用内存映射技术来管理数据库文件。这允许它们将整个数据库文件映射到内存中,以便可以快速读取和写入数据,同时也可以提供持久性存储。

4. 文件编辑器和查看器

许多文本编辑器和文件查看器使用内存映射来加载大型文件,以便可以快速浏览文件内容,而不需要将整个文件加载到内存中。

5. 嵌入式系统

在嵌入式系统中,内存映射可以用于访问硬件寄存器和设备内存,以便与硬件进行通信和控制。

在C语言中进行内存映射操作

在C语言中,内存映射通常使用操作系统提供的API函数来实现。以下是一些典型的步骤,演示如何在C语言中进行内存映射操作。

步骤1:打开文件

首先,您需要打开要映射到内存的文件。您可以使用标准的文件I/O函数,如open(Unix/Linux)或CreateFile(Windows)来打开文件。

#include <stdio.h>
#include <fcntl.h> // For open() function in Unix/Linux
#include <windows.h> // For CreateFile() function in Windowsint main() {// Unix/Linuxint fd = open("myfile.txt", O_RDWR);// WindowsHANDLE fileHandle = CreateFile(L"myfile.txt",GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);// Check for errors and handle file open// Rest of the code...
}

步骤2:获取文件大小

在进行内存映射之前,通常需要获取文件的大小,以确定要映射的内存区域的大小。您可以使用文件I/O函数来获取文件大小。

#include <stdio.h>
#include <sys/stat.h> // For stat() function in Unix/Linux
#include <windows.h>  // For GetFileSize() function in Windowsint main() {// Unix/Linuxstruct stat st;stat("myfile.txt", &st);off_t fileSize = st.st_size;// WindowsLARGE_INTEGER fileSize;GetFileSize(fileHandle, &fileSize);// Check for errors and handle file size// Rest of the code...
}

步骤3:内存映射

一旦您打开文件并确定了要映射的大小,接下来就可以执行内存映射操作。在Unix/Linux中,您可以使用mmap函数,而在Windows中,您可以使用CreateFileMappingMapViewOfFile函数。

在Unix/Linux中进行内存映射:

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>int main() {int fd = open("myfile.txt", O_RDWR);off_t fileSize = lseek(fd, 0, SEEK_END);// Create a memory mappingvoid *mappedData = mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mappedData == MAP_FAILED) {perror("mmap");return 1;}// Now, you can access the file content through 'mappedData'// Rest of the code...// Don't forget to unmap the memory when donemunmap(mappedData, fileSize);close(fd);return 0;
}
在Windows中进行内存映射:
#include <stdio.h>
#include <windows.h>int main() {HANDLE fileHandle = CreateFile(L"myfile.txt",GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (fileHandle == INVALID_HANDLE_VALUE) {perror("CreateFile");return 1;}// Get the file sizeLARGE_INTEGER fileSize;GetFileSize(fileHandle, &fileSize);// Create a file mapping objectHANDLE mapping = CreateFileMapping(fileHandle, NULL, PAGE_READWRITE, 0, 0, NULL);if (mapping == NULL) {perror("CreateFileMapping");CloseHandle(fileHandle);return 1;}// Map the file into memoryvoid *mappedData = MapViewOfFile(mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);if (mappedData == NULL) {perror("MapViewOfFile");CloseHandle(mapping);CloseHandle(fileHandle);return 1;}// Now, you can access the file content through 'mappedData'// Rest of the code...// Don't forget to unmap the memory and close handles when doneUnmapViewOfFile(mappedData);CloseHandle(mapping);CloseHandle(fileHandle);return 0;
}

步骤4:读写数据并同步更改

一旦文件被映射到内存中,您可以像访问内存一样访问文件的内容。您可以使用指针来读取或写入数据,就像操作普通内存一样。

#include <stdio.h>int main() {// Assuming 'mappedData' points to the mapped memory// Read data from the mapped memorychar *data = (char *)mappedData;printf("Data at offset 0: %c\n", data[0]);// Modify data in the mapped memorydata[0] = 'X';// Synchronize changes to the file (Unix/Linux only)msync(mappedData, fileSize, MS_SYNC);// Rest of the code...return 0;
}

在Unix/Linux系统上,您还需要使用msync函数来同步对内存映射的更改。这将确保更改被写入到文件中。

步骤5:解除内存映射和关闭文件

在完成内存映射操作后,不要忘记解除内存映射并关闭文件。这是释放资源和确保数据完整性的重要步骤。

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>int main() {int fd = open("myfile.txt", O_RDWR);off_t fileSize = lseek(fd, 0, SEEK_END);void *mappedData = mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mappedData == MAP_FAILED) {perror("mmap");return 1;}// Read, write, and manipulate data as needed// Unmap the memorymunmap(mappedData, fileSize);// Close the fileclose(fd);return 0;
}

在Windows系统上,也要记得解除内存映射并关闭相关的句柄:

#include <stdio.h>
#include <windows.h>int main() {HANDLE fileHandle = CreateFile(L"myfile.txt",GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (fileHandle == INVALID_HANDLE_VALUE) {perror("CreateFile");return 1;}// Get the file sizeLARGE_INTEGER fileSize;GetFileSize(fileHandle, &fileSize);// Create a file mapping objectHANDLE mapping = CreateFileMapping(fileHandle, NULL, PAGE_READWRITE, 0, 0, NULL);if (mapping == NULL) {perror("CreateFileMapping");CloseHandle(fileHandle);return 1;}// Map the file into memoryvoid *mappedData = MapViewOfFile(mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);if (mappedData == NULL) {perror("MapViewOfFile");CloseHandle(mapping);CloseHandle(fileHandle);return 1;}// Read, write, and manipulate data as needed// Unmap the memoryUnmapViewOfFile(mappedData);// Close handlesCloseHandle(mapping);CloseHandle(fileHandle);return 0;
}

注意事项和最佳实践

在进行内存映射操作时,有一些注意事项和最佳实践需要考虑:

  1. 错误处理:请始终检查API调用的返回值,并处理任何错误。内存映射和文件操作可能会失败,特别是在文件不存在或权限受限的情况下。

  2. 同步更改:在Unix/Linux系统上,更改映射内存后,使用msync函数将更改同步到文件是一个好的做法,以确保数据一致性。

  3. 内存管理:内存映射将文件内容映射到内存中,因此要注意内存的使用情况。确保及时释放内存映射以避免内存泄漏。

  4. 文件锁定:如果多个进程同时访问同一个映射的文件,要确保适当的文件锁定机制以防止竞态条件和数据损坏。

  5. 跨平台注意事项:请注意,内存映射在不同的操作系统上可能有不同的API和行为。要编写可移植的代码,需要了解并处理这些差异。

  6. 数据完整性:内存映射允许直接访问文件内容,因此要小心处理数据,以避免文件损坏或数据丢失。

  7. 关闭文件句柄:在Windows中,确保关闭文件句柄和文件映射对象句柄,以释放系统资源。

总结

内存映射和文件映射是强大的技术,用于在C语言中实现高效的文件操作和共享数据。通过将文件内容映射到内存中,程序可以更轻松地读取和写入数据,提高了性能和灵活性。然而,要注意错误处理、数据完整性和跨平台兼容性等方面的问题。了解这些概念和操作方法将有助于C语言初学者更好地理解和应用内存映射和文件映射技术。

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

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

相关文章

1.7. 找出数组的第 K 大和原理及C++实现

题目 给你一个整数数组 nums 和一个 正 整数 k 。你可以选择数组的任一 子序列 并且对其全部元素求和。 数组的 第 k 大和 定义为&#xff1a;可以获得的第 k 个 最大 子序列和&#xff08;子序列和允许出现重复&#xff09; 返回数组的 第 k 大和 。 子序列是一个可以由其他数…

Linux中getopt函数、optind等变量使用详解

getopt函数、optind等变量使用详解 最近在学习《Unix网络编程》vol2时&#xff0c;发现书中例子经常使用一个命令行解析getopt函数&#xff0c;因为函数声明比较特别&#xff0c;根据自己摸索&#xff0c;遂总结出使用方法。 1. getopt函数的声明 该函数是由Unix标准库提供的…

[应用推荐]Web Scraper——轻量数据爬取利器

对于日常的简单网页内容爬取&#xff0c;学习Python等投入太高&#xff0c;可以考虑使用这个Chrome工具。 以下为收集的具体信息&#xff0c;按需取用。 以下内容来自web ScraperWeb Scraper - The #1 web scraping extensionThe most popular web scraping extension. Start …

博弈论——伯特兰德寡头模型(Bertrand Model)

伯特兰德寡头模型(Bertrand Model) 0 引言 在前面几篇文章中&#xff0c;我们介绍了古诺模型(Cournot duopoly model)和斯塔克尔伯格模型(Stackelberg model) 博弈论——连续产量古诺模型(Cournot duopoly model) 博弈论——斯塔克尔伯格模型(Stackelberg model) 这两个模型…

PyTorch应用实战三:构建神经网络

文章目录 神经网络1.继承Module构建神经网络2.结构化构建神经网络3.函数式操作附&#xff1a;系列文章 神经网络 构建神经网络的一般步骤如下&#xff1a; 确定网络的结构&#xff1a;这包括输入层、输出层和隐藏层的数量以及每层中的节点数等。 收集和准备数据&#xff1a;这…

ARMday2

1~100累加 代码 .text .globl _start _start:mov r0, #1 fun:cmp r0,#100addls r1,r1,r0addls r0,r0,#1b fun .end运行结果

互联网Java工程师面试题·Elasticsearch 篇·第二弹

12、详细描述一下 Elasticsearch 索引文档的过程。 协调节点默认使用文档 ID 参与计算&#xff08;也支持通过 routing &#xff09;&#xff0c;以便为路由提供合适的分片。 shard hash(document_id) % (num_of_primary_shards) 1 、当分片所在的节点接收到来自协调节点…

每日一题 518零钱兑换2(完全背包)

题目 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号整…

API基础————包

什么是包&#xff0c;package实际上就是一个文件夹&#xff0c;便于程序员更好的管理维护自己的代码。它可以使得一个项目结构更加清晰明了。 Java也有20年历史了&#xff0c;这么多年有这么多程序员写了无数行代码&#xff0c;其中有大量重复的&#xff0c;为了更加便捷省时地…

Flv.js编译使用

Flv.js &#xff08;https://github.com/bilibili/flv.js&#xff09;是 HTML5 Flash 视频&#xff08;FLV&#xff09;播放器&#xff0c;纯原生 JavaScript 开发&#xff0c;没有用到 Flash。由 bilibili 网站开源。本文讲述其编译使用。 Flv.js目前最新版本是v1.6.2。在htt…

基于混合蛙跳优化的BP神经网络(分类应用) - 附代码

基于混合蛙跳优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于混合蛙跳优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.混合蛙跳优化BP神经网络3.1 BP神经网络参数设置3.2 混合蛙跳算法应用 4.测试结果…

gitgitHub

在git中复制CtrlInsert、粘贴CtrlShif 一、用户名和邮箱的配置 查看用户名 &#xff1a;git config user.name 查看密码&#xff1a; git config user.password 查看邮箱&#xff1a;git config user.email 查看配置信息&#xff1a; $ git config --list 修改用户名 git co…

Java笔记七(封装,继承与多态)

封装 该露的露&#xff0c;该藏的藏 程序设计追求“高内聚&#xff0c;低耦合”。高内聚就是类的内部数据操作细节自己完成&#xff0c;不允许外部干涉&#xff1b;低耦合&#xff1a;仅暴露少量的方法给外部使用 封装&#xff08;数据的隐藏&#xff09; 通常&#xff0c;…

【Pandas】Apply自定义行数

文章目录 1. Series的apply方法2. DataFrame的apply方法2.1 针对列使用apply2.2 针对行使用apply Pandas提供了很多数据处理的API,但当提供的API不能满足需求的时候,需要自己编写数据处理函数, 这个时候可以使用apply函数apply函数可以接收一个自定义函数, 可以将DataFrame的行…

Linux shell编程学习笔记8:使用字符串

一、前言 字符串是大多数编程语言中最常用最有用的数据类型&#xff0c;这在Linux shell编程中也不例外。 本文讨论了Linux Shell编程中的字符串的三种定义方式的差别&#xff0c;以及字符串拼接、取字符串长度、提取字符串、查找子字符串等常用字符串操作,&#xff0c;以及反…

【2023年11月第四版教材】第18章《项目绩效域》(合集篇)

第18章《项目绩效域》&#xff08;合集篇&#xff09; 1 章节内容2 干系人绩效域2.1 绩效要点2.2 执行效果检查2.3 与其他绩效域的相互作用 3 团队绩效域3.1 绩效要点3.2 与其他绩效域的相互作用3.3 执行效果检查3.4 开发方法和生命周期绩效域 4 绩效要点4.1 与其他绩效域的相互…

20231005使用ffmpeg旋转MP4视频

20231005使用ffmpeg旋转MP4视频 2023/10/5 12:21 百度搜搜&#xff1a;ffmpeg 旋转90度 https://zhuanlan.zhihu.com/p/637790915 【FFmpeg实战】FFMPEG常用命令行 https://blog.csdn.net/weixin_37515325/article/details/127817057 FFMPEG常用命令行 5.视频旋转 顺时针旋转…

System Generator学习——时间和资源分析

文章目录 前言一、目标二、步骤三、步骤 1 &#xff1a;系统生成器的时序分析1、时序分析2、解决时间违规问题 四、步骤 2 &#xff1a;系统生成器中的资源分析总结 前言 在本节实验中&#xff0c;你将学习如何通过在 Simulink 中进行仿真来验证设计的功能&#xff0c;以确保在…

熟练掌握Junit5框架

目录 一、注解 1.1 @Test 1.2 @Disabled 1.3 @BeforeAll 1.4 @AfterAll 1.5 @BeforeEach 1.6 @AfterEach 二、参数化 2.1单参数获取数据 2.2 CSV获取参数 2.3 方法获取数据 2.4 多参数获取数据 2.5测试用例执行顺序 2.6断言 2.6.1断言相等 2.6.2断言不相等

vue3使用v-model控制子组件进行双向数据绑定

vue2写法: 中父组件调用子组件: <child :isShow.sync"isShow" v-show"isShow"/> 子组件想要消失, 在子组件写: this.$emit("update:isShow",false); 具体代码就不粘贴了 vue3写法: 父组件核心代码: v-model:a"xxx" 子组…