【Linux 内核分析课程作业 1】mmap 实现一个 key-valueMap

作业一

功能要求利用 mmap(虚拟内存映射文件) 机制实现一个带持久化能力的 key-valueMap 系统,至少支持单机单进程访问。(可能用到的 linux API: mmap、msync、mremap、munmap、ftruncate、fallocate 等)

电子版提交方式:
2023 年 11 月 20 日 18:00 前通过西电智课平台提交

提交内容

(1) 源代码,包含必要的注释;(2) 简单的说明文件,说明程序如何运行。

邮件主题、附件命名方式:主题:小作业 1-学号 - 姓名 (英文半角,非下划线).
附件:学号 - 姓名.rar,请严格按照命名规范提交!。
联系邮件:xxxxxxx
请勿抄袭,如有雷同,都将以零分计。

代码说明

运行测试结果

$gcc mmapMap.c && ./a.out
强制同步比 0.00 > 0.078 秒
强制同步比 0.10 > 0.276 秒
强制同步比 0.20 > 0.384 秒
强制同步比 0.30 > 0.513 秒
强制同步比 0.40 > 0.663 秒
强制同步比 0.50 > 0.602 秒
强制同步比 0.60 > 0.807 秒
强制同步比 0.70 > 0.775 秒
强制同步比 0.80 > 0.842 秒
强制同步比 0.90 > 0.910 秒
强制同步比 1.00 > 0.972

背景

Linux 内存文件映射是一种将文件内容映射到进程地址空间的技术,它允许进程直接在内存中访问文件,而无需通过 read()​ 或 write()​ 等系统调用进行数据传输。这种技术的核心是 mmap()​ 系统调用,它允许将文件的一部分或全部映射到进程的地址空间,使得文件的内容可以直接通过内存地址来访问和修改。

内存文件映射的主要特点和使用方法:

  1. 直接访问文件内容: 内存文件映射允许进程直接读取和写入文件内容,就好像操作内存一样,而不需要使用标准的文件 I/O 操作(例如 read()​ 和 write()​)。
  2. 性能优势: 由于避免了频繁的系统调用和数据拷贝,因此内存文件映射通常可以提供更好的性能,特别是对于大文件的处理或者需要频繁读写的情况。
  3. 共享内存: 多个进程可以通过内存文件映射共享同一个文件,这对于进程间通信很有用。
  4. 写时复制: 当多个进程映射同一个文件时,对该文件的写操作会使用写时复制技术,每个进程会获得一个文件内容的独立副本,从而避免了相互之间的干扰。

使用 mmap()​ 函数进行内存文件映射时,需要指定文件描述符、映射大小、映射起始位置以及一些其他参数。通常的步骤如下:

  1. 使用 open()​ 函数打开文件,获取文件描述符。
  2. 使用 mmap()​ 函数创建映射,将文件映射到内存中。
  3. 对映射区域进行读写操作。
  4. 使用 munmap()​ 函数取消映射。

可能用到的 linux API: mmap、msync、mremap、munmap、ftruncate、fallocate 介绍:

  1. mmap()
    • void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    • 用于将文件或者设备映射到进程的地址空间。
    • 参数 addr​ 是映射的地址,一般设置为 NULL 以由系统自动选择。
    • length​ 是映射区域的大小。
    • prot​ 表示映射区域的保护方式(读、写、执行等)。
    • flags​ 包含映射的一些特性,比如是否共享、是否采用匿名映射等。
    • fd​ 是要映射的文件描述符。
    • offset​ 是文件中的偏移量。
  2. msync()
    • int msync(void *addr, size_t length, int flags);
    • 用于将指定地址范围的内存数据同步回文件,保持内存和文件内容的一致性。
    • addr​ 是内存区域的起始地址。
    • length​ 是要同步的长度。
    • flags​ 可以指定同步方式,如 MS_ASYNC(异步)、MS_SYNC(同步)等。
  3. mremap()
    • void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, ...);
    • 允许重新调整内存映射的大小和位置。
    • old_address​ 是原映射区域的起始地址。
    • old_size​ 是原映射区域的大小。
    • new_size​ 是新的映射区域大小。
    • flags​ 可以指定一些选项,如 MREMAP_MAYMOVE(允许移动映射)等。
  4. munmap()
    • int munmap(void *addr, size_t length);
    • 用于取消内存映射,释放指定地址区域的内存。
    • addr​ 是要取消映射的起始地址。
    • length​ 是取消映射的长度。
  5. ftruncate()
    • int ftruncate(int fd, off_t length);
    • 用于改变一个打开文件的大小。
    • fd​ 是文件描述符。
    • length​ 是新的文件大小。
  6. fallocate()
    • int fallocate(int fd, int mode, off_t offset, off_t len);
    • 用于为文件分配空间。
    • fd​ 是文件描述符。
    • mode​ 可以指定预留空间或初始化空间。
    • offset​ 是文件中的偏移量。
    • len​ 是要分配的空间大小。

完整代码

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>#define FILENAME "kv_store.dat"/***************************************************************- key-valueMap 数据结构部分**************************************************************/#define MAX_KEY_SIZE 64
#define MAX_VALUE_SIZE 128
#define MAX_ENTRIES 4096struct KeyValue {char key[MAX_KEY_SIZE];char value[MAX_VALUE_SIZE];
};typedef struct keyValueStore {struct KeyValue entries[MAX_ENTRIES];size_t size;
} KeyValueStore;// 增加一个条目
// 返回值:-1 表示失败,>=0 表示成功
int add_entry(KeyValueStore *kv_store, const char *key, const char *value) {if (kv_store->size < MAX_ENTRIES) {// 查重for (size_t i = 0; i < kv_store->size; ++i) {if (strcmp(kv_store->entries[i].key, key) == 0) {return -1;}}struct KeyValue new_entry;strncpy(new_entry.key, key, MAX_KEY_SIZE - 1);strncpy(new_entry.value, value, MAX_VALUE_SIZE - 1);kv_store->entries[kv_store->size++] = new_entry;return kv_store->size - 1;} else {// printf("错误:已达到最大条目数。\n");return -1;}
}// 删除一个条目
// 返回值:-1 表示失败,>=0 表示成功
int delete_entry(KeyValueStore *kv_store, const char *key) {for (size_t i = 0; i < kv_store->size; ++i) {if (strcmp(kv_store->entries[i].key, key) == 0) {// 移动元素来删除memmove(&kv_store->entries[i], &kv_store->entries[i + 1],(kv_store->size - i - 1) * sizeof(struct KeyValue));kv_store->size--;return kv_store->size;}}// printf("错误:找不到项。\n");return -1;
}/***************************************************************- mmap 部分**************************************************************/// 打开一个文件,初始化内存为 mmap, 但是置空
KeyValueStore *mmap_init() {KeyValueStore kv_store;memset(&kv_store, 0, sizeof(KeyValueStore));int fd = open(FILENAME, O_RDWR | O_CREAT,(mode_t)0600); // 如果文件不存在也会新建if (fd == -1) {perror("Error opening file for writing");exit(EXIT_FAILURE);}size_t file_size = sizeof(KeyValueStore);// 转变一个文件的大小if (ftruncate(fd, file_size) == -1) {perror("Error truncating file");exit(EXIT_FAILURE);}void *buf = mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (buf == MAP_FAILED) {close(fd);perror("Error mapping the file");exit(EXIT_FAILURE);}memcpy(buf, &kv_store, sizeof(KeyValueStore));close(fd);return buf;
}// 打开一个文件,初始化内存为 mmap, 为读取的文件内容
KeyValueStore *mmap_load() {int fd = open(FILENAME, O_RDWR); // 文件必须存在if (fd == -1) {perror("Error opening file for writing");exit(EXIT_FAILURE);}size_t file_size = sizeof(KeyValueStore);if (ftruncate(fd, file_size) == -1) {perror("Error truncating file");exit(EXIT_FAILURE);}void *buf = mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (buf == MAP_FAILED) {close(fd);perror("Error mapping the file");exit(EXIT_FAILURE);}close(fd);return buf;
}// 关闭 mmap
void mmap_destroy(KeyValueStore *buf) {size_t file_size = sizeof(KeyValueStore);if (munmap(buf, file_size) == -1) {perror("Error unmapping the file");exit(EXIT_FAILURE);}
}// 强制同步 mmap
void mmap_persist(KeyValueStore *buf) {size_t file_size = sizeof(KeyValueStore);if (msync(buf, file_size, MS_SYNC) == -1) {perror("Error syncing to disk");}
}/***************************************************************- 测试和运行**************************************************************/// 测试运行时间
// - rate: 强制同步比
void test_time(KeyValueStore *buf, double rate) {// Start measuring timeclock_t start = clock();// 操作char key[MAX_KEY_SIZE];char value[MAX_VALUE_SIZE];int r;for (int i = 0; i < 10000; i++) {r = rand() % MAX_ENTRIES;// Addsprintf(key, "key%d", r);sprintf(value, "value%d", r);add_entry(buf, key, value);r = rand() % MAX_ENTRIES;// Deletesprintf(key, "key%d", r);delete_entry(buf, key);// 强制同步if (rate > 0 && i % (int)(1 / rate) == 0) {mmap_persist(buf);}}// 停止计时clock_t end = clock();double elapsed = (double)(end - start) / CLOCKS_PER_SEC;printf("强制同步比 %.2f > %.3f 秒\n", rate, elapsed);
}int main() {KeyValueStore *buf = mmap_init();for (double rate = 0; rate <= 1; rate += 0.1) {test_time(buf, rate);}mmap_destroy(buf);// 打印存储数据KeyValueStore *buf1 = mmap_load();// printf("Stored Data:\n");// for (size_t i = 0; i < buf->size; ++i) {//     printf("Key: %s, Value: %s\n", buf->entries[i].key,//            buf->entries[i].value);// }mmap_destroy(buf1);return 0;
}

参考

Linux mmap 内存映射 - 掘金 (juejin.cn)

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

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

相关文章

IObit Unlocker丨解除占用程序软件

更多内容请收藏&#xff1a;https://rwx.tza-3.xyz 官网&#xff1a;IObit Unlocker “永远不用担心电脑上无法删除的文件。” 界面简单&#xff0c;支持简体中文&#xff0c;一看就会&#xff0c;只需要把无法删除/移动的文件或整个U盘拖到框里就行。 解锁率很高&#xff0c;…

Unity热更新资源和代码-(学前必读)

系列文章目录 在学习完整的热更新流程之前&#xff0c;需要对lua、xlua、AssetBundle有一定的了解&#xff0c;没有基础的同学&#xff0c;可以看之前的博客&#xff0c;有一定的了解后再学习就会事半功倍了 Lua入门课程 AssetBundle入门 xLua入门 前言 在对热更新有初步了解…

Java爬虫的使用案例及简单总结

通过三个简单的案例,来实现的,都是不加验证的情况下. 如果有拼图验证网上也有对应的实现方法自行查找即可. 这里仅仅是一个简单的Demo, 练习使用 0. 爬取网站的配置: article:config:#中央新闻网-三农头条数据部分ntvUrl: https://www.ntv.cn/# 全国农技推广网- 农技动态部分n…

elasticsearch 概述

初识elasticsearch 了解ES elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 例如&#xff1a; 在GitHub搜索代码 在电商网站搜索商品 ELK技术栈 elasticsearc…

简述如何使用Androidstudio对文件进行保存和获取文件中的数据

在 Android Studio 中&#xff0c;可以使用以下方法对文件进行保存和获取文件中的数据&#xff1a; 保存文件&#xff1a; 创建一个 File 对象&#xff0c;指定要保存的文件路径和文件名。使用 FileOutputStream 类创建一个文件输出流对象。将需要保存的数据写入文件输出流中…

Vue3的异步组件使用

通过defineAsyncComponent引入组件&#xff0c;使用Suspense渲染 Suspense有两个插槽&#xff0c;一个default一个fallback <template><Suspense><template #default><userCard></userCard></template><template #fallback>加载中…

我叫:插入排序【JAVA】

1.自我介绍 插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置&#xff0c;以达到排序的目的。 2.继承我的思想 插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包…

WPF ObservableCollection 和 BindingList 有什么区别

ObservableCollection<T>和BindingList<T>都是.NET Framework中的泛型集合类型&#xff0c;它们都支持数据绑定和元素的增加、删除、修改等操作。然而&#xff0c;它们之间存在一些关键的差异&#xff0c;使它们在不同的场景下更有用。 ObservableCollection: Ob…

C#的MessagePack(unity)--02

高级API (MessagePackSerializer) MessagePackSerializer类是MessagePack for C# 的入口点。静态方法构成了MessagePack for C# 的主要API。 APIDescriptionSerialize<T>将一个对象图序列化为MessagePack二进制块。可以使用异步变体获取Stream。也有非泛型重载可用。De…

设计模式—结构型模式之外观模式(门面模式)

设计模式—结构型模式之外观模式&#xff08;门面模式&#xff09; 外观&#xff08;Facade&#xff09;模式又叫作门面模式&#xff0c;是一种通过为多个复杂的子系统提供一个一致的接口&#xff0c;而使这些子系统更加容易被访问的模式。 例子 我们的电脑会有很多 组件&am…

动态页面调研及设计方案

文章目录 vue2 动态表单、动态页面调研一、form-generator二、ng-form-element三、Variant Form四、form-create vue2 动态表单、动态页面调研 一、form-generator 预览&#xff1a;https://mrhj.gitee.io/form-generator/#/ Vue2 Element UI支持拖拽生成表单不支持其他组件…

反电动势对电机起动过程电流的影响

参考链接&#xff1a;反电动势对电机起动过程电流的影响 - 知乎

一个关于proto 文件的经验分享 :gRPC 跨语言双端通信显示错误码:12 UNIMPLEMENTED (附赠gRPC错误码表)

错误现象描述&#xff1a; 在使用c的客户端向golang的服务端发送远程调用时&#xff0c;显示&#xff1a; /home/zry/gRPC/grpc-v1.45.2/examples/cpp/DeviceData/greeter_client.cc83 12: unknown service DeviceData.DeviceDataService Greeter 接收到: RPC 失败这里的unkn…

pytorch的backward()的底层实现逻辑

自动微分是一种计算张量&#xff08;tensors&#xff09;的梯度&#xff08;gradients&#xff09;的技术&#xff0c;它在深度学习中非常有用。自动微分的基本思想是&#xff1a; 自动微分会记录数据&#xff08;张量&#xff09;和所有执行的操作&#xff08;以及产生的新张…

基于梯度算法优化概率神经网络PNN的分类预测 - 附代码

基于梯度算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于梯度算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于梯度优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

细说MySQL数据类型

TOC 目录 MySQL数据类型 数据类型分类 数值类型 tinyint类型 有符号tinyint范围测试 无符号tinyint范围测试 bit类型 bit类型的显示方式 bit类型的范围测试 float类型 有符号float范围测试 无符号float范围测试 decimal类型 字符串类型 char类型 char类型测试 …

Ubuntu 18.04/20.04 LTS 操作系统设置静态DNS

1、nano /etc/systemd/resolved.conf 2、修改配置 使用DNS服务器&#xff1a;223.5.5.5 223.6.6.6 [Resolve] DNS223.5.5.5 223.6.6.6 3、重启服务 systemctl restart systemd-resolved.service 4、查看解析文件 cat /run/systemd/resolve/resolv.conf # This file is man…

Jmeter 如何监控目标服务的系统资源

下载Jmeter插件管理下载 perfmon 将这个插件管理放到Jmeter的\lib\ext目录下 然后重启Jmeter jmeter-plugins-manager-1.10.jar 下载 perfmon插件 添加 io 内存 磁盘的监听 并且添加监听 在宿主机中安装代理监听程序 并启动 ServerAgent.tar.gz

数据结构-插入排序

插入排序 插入排序的三种常见方法&#xff1a; 直接插入排序、折半插入排序、希尔排序。 数据存储结构 因为我们是用的是C语言来实现算法&#xff0c;因此我们需要创建一个结构体&#xff0c;用来存放初始数据。 结构体定义如下&#xff1a; #define MAX 100 typedef int…

设计原则 | 开放封闭原则

一、开放封闭原则&#xff08;OCP&#xff1a;Open-Closed Principle&#xff09; 1、原理 软件实体&#xff08;类、模块、函数等等&#xff09;应该是可以扩展的&#xff0c;但是不可修改的。如果程序中的一处改动就会引发连锁反应&#xff0c;导致一些列相关模块的修改&…