C语言中的磁盘映射与共享内存详解

文章目录

  • C语言中的磁盘映射与共享内存
    • 1. 磁盘映射(Memory Mapping)
      • 1.1 磁盘映射的深入概念
      • 1.2 `mmap`函数的详细参数解析
      • 1.3 磁盘映射的高级应用场景
        • 1.3.1 大文件处理
        • 1.3.2 内存共享
        • 1.3.3 文件与内存同步
        • 1.3.4 内存映射数据库
      • 1.4 完整的磁盘映射代码示例
      • 1.5 注意事项
    • 2. 共享内存(Shared Memory)
      • 2.1 共享内存的深入概念
      • 2.2 POSIX 共享内存
        • 2.2.1 `shm_open` 函数
        • 2.2.2 `ftruncate` 函数
        • 2.2.3 共享内存的映射与解除映射
      • 2.3 完整的共享内存代码示例
      • 2.4 共享内存的同步问题
      • 2.5 共享内存的优缺点
    • 3. 磁盘映射与共享内存的详细比较
    • 4. 性能分析
      • 4.1 磁盘映射的性能
      • 4.2 共享内存的性能
    • 5. 总结



C语言中的磁盘映射与共享内存

在现代操作系统中,进程间通信(IPC)和文件访问的效率至关重要。C语言作为底层系统编程语言,提供了灵活而高效的内存管理技术,其中磁盘映射(Memory Mapping)和共享内存(Shared Memory)是两种非常重要的手段。本篇文章将从更详细的角度探讨这两种技术,分析其原理、实现、应用场景以及性能对比。

1. 磁盘映射(Memory Mapping)

1.1 磁盘映射的深入概念

磁盘映射是操作系统提供的一种将文件的物理地址映射到进程虚拟地址空间的技术。通过这种机制,文件内容可以像访问内存一样直接通过指针操作来读取或修改,避免了传统的readwrite系统调用所带来的性能开销。

通常情况下,文件访问包括以下几个步骤:

  1. 通过read系统调用从文件读取数据。
  2. 系统将数据从磁盘拷贝到内核缓冲区。
  3. 再将数据从内核缓冲区拷贝到用户空间。

而通过mmap,上述步骤被优化为:

  1. 系统将文件的某个部分直接映射到进程的地址空间。
  2. 进程可以通过普通的内存访问操作来直接读取或写入文件内容。
  3. 文件的修改可以通过系统的页回写机制(write-back)同步到磁盘。

因此,磁盘映射的最大优势在于减少了数据的拷贝次数以及I/O系统调用的开销,尤其在处理大文件时,这种技术具有显著的性能提升。

1.2 mmap函数的详细参数解析

为了更好地理解mmap,我们来详细解读一下各个参数的作用:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr:建议映射的起始地址。通常设置为NULL,由内核自动选择合适的地址。如果指定具体地址,内核会尝试在该地址处映射,但若地址无效则映射失败。

  • length:要映射的文件区域的长度。注意,length的值通常应为页面大小(一般为4096字节)的倍数。如果不是,操作系统会对其进行向上对齐。

  • prot:映射区域的保护权限。常见选项包括:

    • PROT_READ:允许读取映射区域。
    • PROT_WRITE:允许写入映射区域。
    • PROT_EXEC:允许执行映射区域中的代码。
    • PROT_NONE:不允许访问该区域。
  • flags:控制映射类型的标志。常见的标志有:

    • MAP_SHARED:多个进程间共享此映射区域,修改会影响到文件。
    • MAP_PRIVATE:私有映射,修改不会影响文件内容。
    • MAP_ANONYMOUS:不映射文件,只分配内存,常用于创建匿名内存区域。
  • fd:要映射的文件的文件描述符,通常由open()函数返回。若使用匿名映射(MAP_ANONYMOUS),则fd应为-1

  • offset:文件映射的起始偏移量,必须是页面大小的倍数。

1.3 磁盘映射的高级应用场景

磁盘映射常用于以下几种高级应用场景:

1.3.1 大文件处理

传统文件读取方式需要多次调用readwrite,在处理大文件时效率较低。而磁盘映射通过一次mmap调用将整个文件映射到内存,使得后续对文件的访问操作变得高效和方便。例如,文本编辑器可以通过mmap直接访问文件,避免反复的I/O操作。

1.3.2 内存共享

多个进程可以通过mmapMAP_SHARED标志共享同一个文件的内存映射区域,从而实现文件级别的进程间通信。这种机制非常适合于日志系统、数据库文件管理等需要多个进程同时访问同一个文件的场景。

1.3.3 文件与内存同步

mmap允许将内存修改同步到文件,而不需要通过write操作。对于需要频繁修改文件内容的场景,磁盘映射能够显著减少内核与用户空间之间的切换开销。

1.3.4 内存映射数据库

许多数据库(如Redis、MongoDB)内部都使用磁盘映射来管理数据存储。通过mmap,数据库系统可以直接将数据文件映射到内存中进行读写操作,既保证了数据的一致性,又提高了访问速度。

1.4 完整的磁盘映射代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>int main() {const char *file_path = "example.txt";int fd = open(file_path, O_RDWR);  // 打开文件,读写模式if (fd == -1) {perror("open");exit(EXIT_FAILURE);}struct stat st;if (fstat(fd, &st) == -1) {perror("fstat");exit(EXIT_FAILURE);}size_t file_size = st.st_size;  // 获取文件大小// 映射文件内容到内存char *mapped = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}// 修改映射内存中的内容strcpy(mapped, "Hello, mmap!");// 打印修改后的内容printf("Modified file content: %s\n", mapped);// 将修改同步回文件if (msync(mapped, file_size, MS_SYNC) == -1) {perror("msync");exit(EXIT_FAILURE);}// 解除映射if (munmap(mapped, file_size) == -1) {perror("munmap");exit(EXIT_FAILURE);}close(fd);  // 关闭文件return 0;
}

1.5 注意事项

  1. 内存泄漏:使用mmap后,必须记得使用munmap解除映射,否则可能会导致内存泄漏。
  2. 性能开销:虽然mmap可以减少数据拷贝的开销,但在频繁进行小文件读写的场景下,mmap的初始化开销反而会高于传统的readwrite操作。因此,它适合处理大文件或频繁访问的场景。
  3. 同步问题:当使用MAP_SHARED时,进程对映射区域的修改并不会立即同步到磁盘,除非显式调用msync或进程退出时自动同步。如果需要保证数据的实时性,请及时使用msync

2. 共享内存(Shared Memory)

2.1 共享内存的深入概念

共享内存是一种高效的进程间通信机制。通过共享内存,多个进程可以直接访问同一个内存区域,实现高速的数据交换。共享内存不经过内核缓冲区,进程之间的数据传递不会涉及数据的拷贝,因而共享内存是所有IPC机制中效率最高的。

共享内存通常用于以下几种场景:

  1. 实时数据传输:需要多个进程频繁交换大量数据,典型应用如视频处理、实时监控系统。
  2. 多进程并发编程:多个进程共享同一段数据,在多核CPU上可以最大化利用并行计算能力。
  3. 内存映射数据库:共享内存常用于大型数据库系统中,用于进程间共享内存中的数据。

2.2 POSIX 共享内存

POSIX共享内存通过shm_openftruncatemmap等函数来创建和管理共享内存。

2.2.1 shm_open 函数

shm_open用于创建或打开共享内存对象:

int shm_open(const char *name, int oflag, mode_t mode);
  • name:共享内

存对象的名字,必须以/开头,如/my_shm

  • oflag:控制打开方式,常用选项包括:
    • O_CREAT:创建共享内存对象。
    • O_RDWR:可读可写。
  • mode:与文件权限类似,指定共享内存的访问权限。
2.2.2 ftruncate 函数

ftruncate用于调整共享内存对象的大小。在创建共享内存对象后,默认大小为0,因此需要调用ftruncate设置适当的大小。

2.2.3 共享内存的映射与解除映射

和文件映射一样,mmap可以将共享内存对象映射到进程的地址空间,munmap则用于解除映射。

2.3 完整的共享内存代码示例

以下是一个使用共享内存的例子,展示父子进程如何通过共享内存交换数据:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>#define SHM_NAME "/my_shared_memory"
#define SHM_SIZE 4096int main() {int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open");exit(EXIT_FAILURE);}// 调整共享内存大小if (ftruncate(shm_fd, SHM_SIZE) == -1) {perror("ftruncate");exit(EXIT_FAILURE);}// 映射共享内存char *shared_mem = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (shared_mem == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}pid_t pid = fork();  // 创建子进程if (pid == 0) {// 子进程:向共享内存写入数据const char *message = "Hello from child process!";memcpy(shared_mem, message, strlen(message) + 1);printf("Child process wrote message: %s\n", message);} else if (pid > 0) {// 父进程:等待子进程完成wait(NULL);// 读取共享内存中的数据printf("Parent process read message: %s\n", shared_mem);// 解除映射并删除共享内存munmap(shared_mem, SHM_SIZE);shm_unlink(SHM_NAME);} else {perror("fork");exit(EXIT_FAILURE);}return 0;
}

2.4 共享内存的同步问题

共享内存的一个重要特性是速度极快,但这也带来了一些潜在问题,特别是数据同步和并发控制。在多个进程同时访问共享内存时,容易发生竞争条件(race condition)。为了避免这种问题,通常需要配合使用**信号量(semaphore)互斥锁(mutex)**来进行并发控制。

2.5 共享内存的优缺点

优点

  • 极高的通信效率:没有数据拷贝,进程直接访问共享的内存区域。
  • 大数据传输的利器:适合频繁交换大量数据的场景。

缺点

  • 同步问题复杂:多个进程访问共享内存时容易发生竞争,可能需要借助锁机制来保证数据一致性。
  • 跨机器不可用:共享内存仅在同一台机器的进程间有效,无法用于分布式系统。

3. 磁盘映射与共享内存的详细比较

特性磁盘映射共享内存
数据存储文件数据映射到内存多个进程共享同一块内存
典型应用场景大文件读取、内存映射数据库、文件共享进程间高速通信、视频处理、实时数据传输
性能适合大文件按需加载进程间通信最快的方式之一
共享机制文件级别共享,通过MAP_SHARED实现内存级别共享,通过shm_openmmap实现
数据持久性文件修改可以同步到磁盘数据仅存在内存,不持久化
并发问题文件映射多为只读,少有并发问题需要处理进程间的竞争条件

4. 性能分析

4.1 磁盘映射的性能

磁盘映射在处理大文件时性能非常优越。由于其减少了文件I/O的系统调用次数,并支持按需加载,因此在处理大文件时,可以大幅降低文件访问的时间开销。此外,磁盘映射还避免了数据在内核与用户空间的多次拷贝,进一步提升了性能。

4.2 共享内存的性能

共享内存是进程间通信中最快的一种方式,因为它直接通过内存来交换数据,而不需要通过内核缓冲区。因此,在需要频繁进行进程间通信或大数据传输的场景中,使用共享内存能够显著提高程序的性能。不过需要注意的是,多个进程同时操作共享内存时,必须通过加锁机制来保证数据的一致性,这会带来一些性能开销。

5. 总结

磁盘映射和共享内存是C语言中两种重要的内存管理技术。磁盘映射适用于大文件处理和文件共享,而共享内存则用于高效的进程间通信。根据具体的应用场景合理选择这两种技术,可以极大地提高系统的性能和运行效率。

对于需要处理大文件、频繁访问文件或进行文件共享的场景,磁盘映射是非常合适的选择。而对于需要进程间高速通信、实时数据传输的应用,共享内存则是最佳选择。

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

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

相关文章

np.ndarray和np.array区别;MXNet的 mx.array 类型是什么;NDArray优化了什么:并行计算优化

目录 np.ndarray和np.array区别 np.ndarray np.array 举例说明 MXNet的 mx.array 类型是什么 NDArray优化了什么 1. 异步计算和内存优化 2. 高效的数学和线性代数运算 3. 稀疏数据支持 4. 自动化求导 举例说明 np.ndarray和np.array区别 在NumPy库中,np.ndarray和n…

如何看待IBM中国研发部裁员?

如何看待IBM中国研发部裁员&#xff1f;近日&#xff0c;IBM中国宣布撤出在华两大研发中心&#xff0c;引发了IT行业对于跨国公司在华研发战略的广泛讨论。这一决定不仅影响了众多IT从业者的职业发展&#xff0c;也让人思考全球化背景下中国IT产业的竞争力和未来发展方向。面对…

Java+vue的医药进出口交易系统(源码+数据库+文档)

外贸系统|医药进出口交易系统 目录 基于Javavue的服装定制系统 一、前言 二、系统设计 三、系统功能设计 仓储部门功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设…

2024.09.04 校招 实习 内推 面经

&#x1f6f0;️ &#xff1a;neituijunsir 交* 流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 海康威视2025届校园招聘正式启动&#xff08;内推&#xff09; 校招 | 海康威视2025届校园招聘正式启动&#xff08;内推&#xff09; 2、校招 | 沃飞长空2025届全球校…

中国书法——孙溟㠭浅析碑帖《三希堂法帖》

孙溟㠭浅析碑帖《三希堂法帖》 全称是《三希堂石渠宝笈法帖》&#xff0c;是中国清代宫廷刻帖&#xff0c;一共三十二册。 清朝高宗弘历收藏了晋王羲之《快雪时晴帖》&#xff0c;王献之的《中秋帖》&#xff0c;王珣的《伯远帖》三种王氏原墨迹。故而把所藏法书之所…

农产品管理与推荐系统Python+Django网页界面+计算机毕设项目+推荐算法

一、介绍 农产品管理与推荐系统。本系统使用Python作为主要开发语言&#xff0c;前端使用HTML&#xff0c;CSS&#xff0c;BootStrap等技术和框架搭建前端界面&#xff0c;后端使用Django框架处理应用请求&#xff0c;使用Ajax等技术实现前后端的数据通信。实现了一个综合性的…

2024年9月10日嵌入式学习

今日主要学习了缓冲帧。 Framebuffer&#xff08;帧缓冲&#xff09;是Linux系统为显示设备提供的一套应用程序接口&#xff0c;它将显存抽象为一种设备&#xff0c;允许上层应用程序在图形模式下直接进行显示缓冲区的读写操作。 原理&#xff1a;通过内存映射技术向显存空间…

MM-PhyQA——一个专门处理高中物理选择题的 LLM 聊天机器人

概述 论文地址&#xff1a;https://arxiv.org/abs/2404.12926 人工智能的发展正在改变我们的学习方式。特别是使用大规模语言模型&#xff08;LLM&#xff09;的聊天机器人&#xff0c;通过提供个性化指导和即时反馈&#xff0c;极大地拓展了教育的可能性。 然而&#xff0c…

带你深入了解C语言指针(一)

目录 前言 一、内存和地址 1. 内存 2. 究竟该如何理解编址 二、指针变量和地址 1. 取地址操作符&#xff08;&&#xff09; 2. 指针变量和解引用操作符&#xff08;*&#xff09; 2.1 指针变量 2.2 如何拆解指针类型 2.3 解引⽤操作符 3. 指针变量的大小 三、指…

JavaScript更改属性名称+增加字段+排序

JavaScript更改属性名称增加字段排序 背景 客户提供的接口里包含了一堆数据&#xff0c;其中分为多个模块&#xff0c;需要进行拆分&#xff0c;其中涉及到名称更改、字段增加、排序。处理过程 -需要的数据&#xff1a; data: {"四年级": [{ "class": &q…

LeetCode题练习与总结:矩形面积--223

一、题目描述 给你 二维 平面上两个 由直线构成且边与坐标轴平行/垂直 的矩形&#xff0c;请你计算并返回两个矩形覆盖的总面积。 每个矩形由其 左下 顶点和 右上 顶点坐标表示&#xff1a; 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。第二个矩形由其左…

jina-embeddings 的使用教程,怎么用它做embeddings和rerank的操作呢?

Jina-embeddings 使用教程 Jina-embeddings 是一个强大的工具&#xff0c;可以用来生成文本的嵌入向量&#xff08;embeddings&#xff09;&#xff0c;这些向量可用于相似度搜索、分类、重排序&#xff08;reranking&#xff09;等任务。在这个教程中&#xff0c;我将展示如何…

配置 MinGW 以及使用 g++ 编译 C++ 程序

如何在 Windows 上安装和配置 MinGW 以及使用 g 编译 C 程序 (C语言&#xff08;gcc&#xff09;类似 ) 在Windows环境下&#xff0c;使用C进行编程需要一个编译器&#xff0c;而MinGW (Minimalist GNU for Windows) 是一个常用的C/C编译器工具集。对于编程新手来说&#xff0c…

SOMEIP_ETS_101: SD_ClientServiceActivate_send_StopOfferService

测试目的&#xff1a; 验证当DUT在客户端模式下开始发送FindService消息时&#xff0c;测试器发送StopOfferService后&#xff0c;DUT能够理解其正在寻找的服务和实例ID不再可用&#xff0c;并停止为此服务和实例ID发送FindService消息。同时&#xff0c;DUT仍然可以发送Find-…

云曦2024秋季开学考

ezezssrf 第一关&#xff1a;md5弱比较 yunxi%5B%5D1&wlgf%5B%5D2 第二关&#xff1a; md5强比较 需要在bp中传参&#xff0c;在hackbar里不行 yunxiiM%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DC V%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_B…

【HarmonyOS NEXT】实现网络图片保存到手机相册

【问题描述】 给定一个网络图片的地址&#xff0c;实现将图片保存到手机相册 【API】 phAccessHelper.showAssetsCreationDialog【官方文档】 https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#showassetscreationdialog…

降维打击 华为赢麻了

文&#xff5c;琥珀食酒社 作者 | 积溪 真是赢麻了 华为估计都懵了 这辈子还能打这么富裕的仗&#xff1f; 其实在苹果和华为的发布会召开之前 我就知道华为肯定会赢 但我没想到 苹果会这么拉胯 华为这是妥妥的降维打击啊 就说这苹果iPhone 16吧 屏幕是变大了、颜色…

编译安装调试 scaLapack 和 openmpi 以及 lapack

编译安装调试 scaLapack /home/hipper/ex_scalapack/ mkdir ./lapack mkdir -p ./lapack/local/lib mkdir ./openmpi mkdir ./scalapack 1&#xff0c;编译安装 Lapack 下载代码&#xff1a; cd lapack wget https://github.com/Reference-LAPACK/lapack/archive/refs/tags/…

Python | Leetcode Python题解之第398题随机数索引

题目&#xff1a; 题解&#xff1a; class Solution:def __init__(self, nums: List[int]):self.nums numsdef pick(self, target: int) -> int:ans cnt 0for i, num in enumerate(self.nums):if num target:cnt 1 # 第 cnt 次遇到 targetif randrange(cnt) 0:ans …

逐行解析多头注意力机制

多头注意力机制是NLP算法岗常考的代码题&#xff0c;本篇文章将逐行梳理多头注意力机制的代码。 全部代码 import math import torch import torch.nn as nnclass MultiHeadAttention(nn.Module):def __init__(self, d_model, nums_head):super(MultiHeadAttention, self).__i…