当前位置: 首页 > news >正文

【实时Linux实战系列】规避缺页中断:mlock/hugetlb 与页面预热

在实时系统中,程序的执行路径需要尽可能地避免被中断打断,以保证系统的实时性和响应性。缺页中断是导致程序中断的常见原因之一,它发生在程序试图访问未加载到物理内存中的页面时。为了避免这种中断,可以使用 mlockmlockall 系统调用、MAP_LOCKED 标志以及 HugeTLB 和透明大页(THP)等技术来确保关键路径上的页面始终驻留在物理内存中。此外,页面预热策略也可以帮助减少缺页中断的发生。

项目背景与重要性

在实时系统中,如工业自动化、航空航天、金融交易等领域,程序的实时性和响应性至关重要。缺页中断会导致程序执行的延迟,从而影响系统的实时性。因此,掌握如何使用 mlock、HugeTLB 和页面预热等技术来规避缺页中断,对于开发者来说具有重要的价值。

掌握此技能的重要性

  1. 提高实时性:通过确保关键路径上的页面始终驻留在物理内存中,可以减少程序执行的延迟,提高系统的实时性。

  2. 增强可靠性:减少缺页中断的发生,可以提高系统的稳定性和可靠性。

  3. 优化性能:通过使用大页技术,可以减少页表的大小,提高内存访问的效率,从而优化系统的性能。

核心概念

在深入实践之前,我们需要了解一些与主题相关的基本概念和术语。

mlock 和 mlockall

  • mlockmlock 系统调用可以锁定指定的内存区域,防止这些页面被交换到磁盘上。

  • mlockallmlockall 系统调用可以锁定调用进程的整个地址空间,防止这些页面被交换到磁盘上。

MAP_LOCKED

MAP_LOCKEDmmap 系统调用的一个标志,用于将映射的内存区域锁定在物理内存中,防止这些页面被交换到磁盘上。

HugeTLB 和透明大页(THP)

  • HugeTLB:HugeTLB 是一种使用大页的内存管理机制,可以减少页表的大小,提高内存访问的效率。

  • 透明大页(THP):透明大页是一种自动化的内存管理机制,内核会自动将多个小页合并为一个大页,以提高内存访问的效率。

页面预热

页面预热是指在程序开始运行之前,预先将需要访问的页面加载到物理内存中。通过页面预热,可以减少程序运行时的缺页中断。

环境准备

在开始实践之前,我们需要准备以下软硬件环境。

操作系统

  • Linux:建议使用 Ubuntu 20.04 或更高版本,因为这些版本提供了最新的内核和开发工具。

开发工具

  • GCC:用于编译 C 程序。可以通过以下命令安装:

  • sudo apt-get update
    sudo apt-get install build-essential

环境配置

确保你的系统已经安装了上述工具,并且可以通过命令行访问它们。可以通过以下命令检查 GCC 是否安装成功:

gcc --version

实际案例与步骤

接下来,我们将通过一个具体的案例来展示如何使用 mlock、HugeTLB 和页面预热等技术来规避缺页中断。我们将创建一个简单的程序,该程序在运行时确保关键路径上的页面始终驻留在物理内存中。

步骤 1:使用 mlock 锁定内存

首先,我们将使用 mlock 系统调用来锁定指定的内存区域。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>#define PAGE_SIZE 4096
#define NUM_PAGES 1024int main() {// 分配内存void *ptr = malloc(PAGE_SIZE * NUM_PAGES);if (ptr == NULL) {perror("malloc");exit(EXIT_FAILURE);}// 锁定内存if (mlock(ptr, PAGE_SIZE * NUM_PAGES) == -1) {perror("mlock");free(ptr);exit(EXIT_FAILURE);}// 使用内存,避免优化掉memset(ptr, 0, PAGE_SIZE * NUM_PAGES);printf("Memory locked and used\n");// 保持程序运行一段时间sleep(10);// 解锁内存if (munlock(ptr, PAGE_SIZE * NUM_PAGES) == -1) {perror("munlock");}free(ptr);return 0;
}

步骤 2:使用 mlockall 锁定整个地址空间

接下来,我们将使用 mlockall 系统调用来锁定调用进程的整个地址空间。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>#define PAGE_SIZE 4096
#define NUM_PAGES 1024int main() {// 分配内存void *ptr = malloc(PAGE_SIZE * NUM_PAGES);if (ptr == NULL) {perror("malloc");exit(EXIT_FAILURE);}// 锁定整个地址空间if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {perror("mlockall");free(ptr);exit(EXIT_FAILURE);}// 使用内存,避免优化掉memset(ptr, 0, PAGE_SIZE * NUM_PAGES);printf("Entire address space locked and used\n");// 保持程序运行一段时间sleep(10);// 解锁整个地址空间if (munlockall() == -1) {perror("munlockall");}free(ptr);return 0;
}

步骤 3:使用 MAP_LOCKED 锁定映射的内存区域

接下来,我们将使用 mmap 系统调用的 MAP_LOCKED 标志来锁定映射的内存区域。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>#define PAGE_SIZE 4096
#define NUM_PAGES 1024int main() {// 映射内存void *ptr = mmap(NULL, PAGE_SIZE * NUM_PAGES, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0);if (ptr == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}// 使用内存,避免优化掉memset(ptr, 0, PAGE_SIZE * NUM_PAGES);printf("Memory mapped and locked\n");// 保持程序运行一段时间sleep(10);// 取消映射if (munmap(ptr, PAGE_SIZE * NUM_PAGES) == -1) {perror("munmap");}return 0;
}

步骤 4:使用 HugeTLB 分配大页

接下来,我们将使用 HugeTLB 来分配大页内存。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>#define PAGE_SIZE 4096
#define NUM_PAGES 1024int main() {// 映射大页内存void *ptr = mmap(NULL, PAGE_SIZE * NUM_PAGES, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);if (ptr == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}// 使用内存,避免优化掉memset(ptr, 0, PAGE_SIZE * NUM_PAGES);printf("Huge page memory mapped\n");// 保持程序运行一段时间sleep(10);// 取消映射if (munmap(ptr, PAGE_SIZE * NUM_PAGES) == -1) {perror("munmap");}return 0;
}

步骤 5:页面预热

最后,我们将通过页面预热策略来减少缺页中断的发生。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>#define PAGE_SIZE 4096
#define NUM_PAGES 1024void touch_pages(void *ptr, size_t size) {char *p = (char *)ptr;for (size_t i = 0; i < size; i += PAGE_SIZE) {p[i] = 0; // 触发页面加载}
}int main() {// 分配内存void *ptr = malloc(PAGE_SIZE * NUM_PAGES);if (ptr == NULL) {perror("malloc");exit(EXIT_FAILURE);}// 页面预热
touch_pages(ptr, PAGE_SIZE * NUM_PAGES);printf("Memory preheated\n");// 保持程序运行一段时间
sleep(10);free(ptr);
return 0;

}


### 代码说明- **`mlock` 和 `mlockall`**:锁定指定的内存区域或整个地址空间,防止这些页面被交换到磁盘上。
- **`MAP_LOCKED`**:在 `mmap` 时使用 `MAP_LOCKED` 标志,锁定映射的内存区域。
- **`HugeTLB`**:使用 `MAP_HUGETLB` 标志在 `mmap` 时分配大页内存。
- **`touch_pages`**:页面预热函数,通过访问每个页面来触发页面加载。## 常见问题与解答### 问题 1:`mlock` 和 `mlockall` 的权限问题**解答**:`mlock` 和 `mlockall` 需要特定的权限。在默认情况下,只有具有 `CAP_IPC_LOCK` 能力的进程才能使用这些调用。可以通过以下命令为当前用户设置该能力:
```bash
sudo setcap cap_ipc_lock=ep your_program

问题 2:如何确定系统支持大页?

解答:可以通过以下命令检查系统是否支持大页:

cat /sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages

如果返回值大于 0,则表示系统支持大页。

问题 3:页面预热是否总是必要的?

解答:页面预热在某些情况下是必要的,尤其是在程序启动时需要快速响应的情况下。然而,在某些情况下,页面预热可能会增加内存的使用量,因此需要根据具体情况进行权衡。

实践建议与最佳实践

调试技巧

  • 使用 vmstat:通过 vmstat 命令监控内存使用情况,检查缺页中断的发生频率。

  • 使用 strace:通过 strace 命令跟踪系统调用,检查程序是否正确使用了 mlockmmap

性能优化

  • 合理使用大页:在需要高内存访问效率的场景下,合理使用大页可以显著提高性能。

  • 避免不必要的锁定:避免锁定不必要的内存区域,以减少内存的使用量。

常见错误解决方案

  • 权限不足:确保程序具有 CAP_IPC_LOCK 能力。

  • 大页不足:确保系统有足够的大页可用。

总结与应用场景

通过本文的介绍,我们学习了如何使用 mlock、HugeTLB 和页面预热等技术来规避缺页中断。这些技术可以确保关键路径上的页面始终驻留在物理内存中,减少程序执行的延迟,提高系统的实时性和响应性。在实际应用中,这种技术可以应用于工业自动化、航空航天、金融交易等领域,帮助开发者构建高性能的实时系统。

希望读者能够将所学知识应用到真实项目中,通过实践不断提升自己的编程能力和技术水平。


文章转载自:

http://bxcTlPKU.crkhd.cn
http://bsoQfHVk.crkhd.cn
http://qSoKCcL8.crkhd.cn
http://24QNAojU.crkhd.cn
http://skcDmCo8.crkhd.cn
http://stseAUCC.crkhd.cn
http://bGhiE7m0.crkhd.cn
http://ZsIh1aXX.crkhd.cn
http://9vrgj37v.crkhd.cn
http://mYw9LJlB.crkhd.cn
http://AcVnsKvf.crkhd.cn
http://wDp4rRB0.crkhd.cn
http://UbrMX9jc.crkhd.cn
http://aK2ionFt.crkhd.cn
http://Olou7gcx.crkhd.cn
http://0v9sajIr.crkhd.cn
http://nwkI7EF3.crkhd.cn
http://Y5MkX4Co.crkhd.cn
http://HAxQm8kv.crkhd.cn
http://RhdkUe3t.crkhd.cn
http://xHUU1ala.crkhd.cn
http://ksrOHrnm.crkhd.cn
http://63hYVBz0.crkhd.cn
http://w2Z6Y49G.crkhd.cn
http://TUV9Jvaj.crkhd.cn
http://Oc4aJJLC.crkhd.cn
http://4ejPQz3B.crkhd.cn
http://qridArlK.crkhd.cn
http://5SJ6JwPY.crkhd.cn
http://wK6Hec9N.crkhd.cn
http://www.dtcms.com/a/378676.html

相关文章:

  • 全球汽车高压电加热器市场规模到2031年将达到62.72亿美元,CAGR 25.2%
  • 【展厅多媒体】从技术到体验,AR在展厅中的一体化整合
  • 双指针算法_移动零
  • 数据结构之复杂度
  • 几种常用锁
  • android13修改WiFi扫描二维码识别识别成功率不高的问题
  • params和body传参讲解
  • 单片机学习笔记
  • 图卷积神经网络(GCN)学习笔记
  • MySQL执行过程中如何选择最佳的执行路径
  • 牛客周赛 Round 108(思维、位运算、DP、SOSDP)
  • 插槽 el-input 数据双向 绑定失效 响应式更新失败
  • 代码随想录算法训练营第58天 | 拓扑排序精讲、dijkstra(朴素版)精讲
  • 揭秘KafkaStreams 线程缓存:NamedCache深度解析
  • 中标麒麟7.4部署gitlab-runner
  • Shopify指纹手机矩阵:无限扩店,横扫FB/GG广告封号风险
  • react context如何使用
  • npm是什么?优缺点又是什么?
  • ubuntu24.04+5070ti训练yolo模型(2)
  • [SQL]查询SSMS当前连接数据库列表
  • 乾博绝缘监测仪为水泥厂安全生产护航
  • JVM(jdk1.8) 实战
  • 设计模式(C++)详解—工厂方法模式(2)
  • 自动化运维实践:SaaS系统Nginx配置文件自动化运维脚本详解
  • 3D模型快速混沌加密matlab完整代码
  • 敏捷实践指南(中文版):章节梳理/主要知识体系和知识点
  • 第一章 ELK Stack基础概念与架构
  • GraphQL RPC 与通用索引器公测介绍:为 Sui 带来更强大的数据层
  • ShardingSphere 分库分表技术实现与实战案例
  • Docker 部署 MongoDB:单节点与副本集的最佳实践