Linux进程内存用量分析之内存映射篇
nanshan 2024-10-09 13:02 20 浏览 0 评论
一、背景
搜索引擎中,使用内存映射的方式加载庞大的索引数据文件已是常态,58自研的搜索引擎ESearch也是如此。ESearch检索节点中,索引数据是分段存储的,段上正排倒排等数据又分别存储在不同文件上,所有索引数据都使用内存映射的方式读取或更新。这样做有许多好处,比如简化了数据加载逻辑,由操作系统管理磁盘缓存等。但是也存在一些问题,比如映射的内存用量不容易控制等。
?ESearch检索节点索引数据的常驻内存用量占服务总常驻内存的60%左右,所以分析和监控内存映射文件的内存用量很有必要的,一方面有助于了解索引的内存使用情况,辅助进行内存优化,提高资源利用率;另一方面也有助于排查I/O相关问题,比如当发生I/O过高问题时,通过各文件映射的内存变化量来分析服务在读取哪些文件等。
本文将介绍两种与内存映射文件相关的内存用量分析方法:1.分析磁盘文件内容加载到物理内存中的总量,可辅助定位I/O相关问题等。2.分析进程使用到的文件数据的内存用量,可帮助了解进程的内存资源、文件的热数据分布等。下面会结合Linux内存管理机制进行介绍。
二、原理分析
为了更加系统的了解内存统计工具和方法,在这一章节对内存映射、Page Cache、页表(Page Table)等相关原理做简单描述。
首先介绍下Page Cache,它把一部分内存用于缓存磁盘数据,减少访问磁盘时间。它与文件系统和内存管理系统有密切关联。
接下来介绍页表,页表是进程中不可或缺的一部分,用于把虚拟地址空间映射到物理地址空间。页表需要存储在连续的物理内存上,X86机器单层页表的情况下,想映射3G用户虚拟空间,需要连续768个大小为4k的物理页。所以同时运行N个进程时,需要N块大小为768页帧的连续内存,在x64中这种情况变得更加严重。
绝大多数进程只会使用整个虚拟地址空间的一小部分,并且这一小部分还是非常离散地分布在整个地址空间范围内,根据进程运行的这个特点,Linux内核设计了四层页表,分别为Page Global Directory(PGD)、Page UpperDirectory(PUD)、Page MiddleDirector(PMD)、Page Table Entry(PTE)。多层页表对比一层页表的一个优势在于对虚拟地址空间中不必要的区域,在PMD或者PTE层中不必创建没用的PMD节点或者PTE节点,多级页表节省了大量内存。另一个优点在于x64及其中适配四级硬件页表,可以增大寻址范围。但是对于不同架构,页表的层次也不同,有的架构只需要实现二层或三层页表。在这里我们以三层举例,如图:
接下来通过内存映射将页表、PageCache、内存映射相关知识串联起来。
2.1 内存映射
ESearch使用mmap的方式打开磁盘文件,将磁盘文件映射到虚拟内存中,如下图所示:
加载磁盘页主要经过页表和Page Cache两层结构,关系图如下:
根据上图模拟访问内存映射中的一段虚拟内存的过程。
1. 先将虚拟地址0x00445500转换成虚拟页,再根据地址映射原理在页表寻找所对应的页帧中是否存在物理页地址。假设页表中0x0044页帧(page frame)内没有指向物理页的指针(具体表现为对应PTE有效位为0);
2.1. 如果存在有效物理页地址,将物理页对应数据返回给用户空间;
2.2. 如果不存在有效物理页地址,内核将会到Page Cache查找;
3. 进程根据打开的文件描述符找到文件的inode数据结构;
4. inode包含了i_mapping域,i_mapping存放一个指向address_space对象的指针,address_space对象是承接文件到基数树的桥梁,能让进程通过inode快速定位到需要查找的物理内存页;基数树是多叉树的一种,Page Cache使用基数树快速定位物理页的位置以及判断物理页是否被加载到内存;
5. 根据文件偏移量,定位到需要访问的页,通过address_space查找该页是否被缓存;
6. address_space中包含了指向基数树根节点(rnode)的指针page_tree,根据rnode可以找到基数树的最高层节点,然后快速查找需要访问的页是否被缓存在物理内存中。基数树结构如下图所示,快速查询方法类似于页表虚拟地址映射物理页的方法;
7.1. 如果定位到,则说明已经缓存,直接将指向该物理页的指针加载到页表中对应的页帧中即可;
7.2 如果没有找到,则发生缺页中断,创建一个页缓存页(假设为0xff007700),同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重复查找页缓存。
2.2 Page Cache 和页表的不同
内存映射中加载页的统计分为两个层级:
1.内核层级:Page Cache保存了多少该文件的磁盘页在内存;
2.进程层级:进程使用了多少该文件的磁盘页,也就是页表中有多少关于该文件的有效物理页。
对于某个进程使用到的内存映射文件,使用页表统计得到的就是当前进程使用到的页;使用Page Cache统计,还会包括预读带入的页、其他进程缓存的页等。对于同一文件二者的加载量一般不相同,一般情况下进程页表加载页的数量都小于等于PageCache加载页的数量。
接下来介绍用mincore系统调用统计Page Cache物理内存用量和用/proc目录下的文件来统计页表中内存映射区间内物理内存加载情况。
2.3 mincore系统调用
mincore系统调用原型是int mincore(void *addr, size_tlength, unsigned char *vec),第一个参数是mmap返回出来的指针,第二个参数是文件长度,第三个参数是uint8数组,该数组是返回值。mincore会统计从addr开始长度为 length字节的虚拟内存页,通过vec返回这些页所映射的数据是否被加载到物理内存中。该页数据在内存中,vec对应的元素就为1,否则为0。mincore系统调用能够很方便的拿到该页是否在PageCache中。接下来介绍页表用量的统计,就使用了/proc目录下的文件。
2.4 /proc目录下的文件
Linux通过/proc目录,以虚拟文件系统的方式提供了访问内核数据的途经。/proc/<pid>目录下提供了每个进程的各种信息,其中我们用到与内存相关的主要有smaps,maps,pagemap三个文件。以下一一介绍它们的用法和不同之处,以及它们之间的优缺点比较。注意一点:只有在这三个文件被访问的时候,内核才会进行统计并在内存中生成文件内容,因此它们平时不会占用内核的计算资源和存储空间。
2.4.1 smaps
/proc/<pid>/smaps其中pid是运行进程的进程号,smaps里面表示着所有内存映射的信息,比如动态链接库,new/malloc使用mmap打开的内存空间,内存映射的文件和栈空间等。下图为smaps输出的一部分,这部分是ESearch服务正排索引文件加载情况的示意图。
其中Size表示所占虚拟内存大小(总大小),Rss表示加载到物理内存的大小。让进程分析自己进程号的smaps文件,得到对应文件真实加载到页表的Rss。利用这样的方式可以获取文件部分加载到页表中的大小。
但是需要注意的是如果在高频统计内存段和其他线程访问的内存段重叠的情况下,对smaps文件进行读操作甚至外部去cat smaps 都会对其他线程造成较大影响。阅读源码时发现生成smaps有大粒度的内存锁的操作,具体一起看代码,以下源码做了精简,生成smaps文件关键源码如下:
代码大致含义如下:
1. 使用ptr_offset_map_lock加锁addr开始到end结束的内存段,该函数会独占内存区间对应的页表;
2. 返回addr对应的页表偏移量的pte,pte变量中保存的是当前addr对应的那一页;
3. for循环内存地址,每次addr加PageSize大小,pte++表示下一个页,for循环内部是给mss赋值,mss内累加了addr到end范围内的每页的状态;
4. 解除对应区间页表占用。
下图为每个mmap打开地址段的标准化输出函数:
每次show_smap都会调用smaps_walk,smaps_walk将会调用smaps_ptr_range来得到addr到end范围内所有页的状况,再用seq_printf格式化。
循环调用show_smap函数就能生成完整的smaps文件内容。以上就是smaps统计一段用内存映射打开的文件中所有页的方法。
总结:
优势:smaps文件易于观察,统计信息丰富,并且容易获得;
缺点:因为生成smaps过程中会多次调用smaps_ptr_range,该过程中都独占该进程页表的一部分,别的线程不能访问。所以读取smaps文件不适用于频繁的内存读写场景。
2.4.2 pagemap
在/proc/<pid>/pagemap中保存着pid进程对应的页表,可以读取该文件的页表信息来计算进程需要的页有多少。首先需要查看源码来判断pagemap文件是否跟smaps文件一样,在生成过程中都需要加锁。以下为关键源码:
1. 从walk中拿出上层定义的pm对象,该对象为pagemapread型,该数据结构中保存了一个指向u64数组的指针,该数组用来保存最终结果;
2. for循环以PageSize为步长,遍历整个从addr到end的内存区间,for循环中ptr_offset_map和pte_to_pagemap_entry都是在填充pfn这个u64类型的变量;
3. 由add_to_pagemap把pfn对象的内容填充到保存最终结果的pm结构体中。根据源码可以知道生成pagemap过程是不加锁的,所以读取该文件比较适合于统计内存占用量这种对数据一致性要求不高的场景。pagemap中每64位代表着当前pid进程页表中对应页的状态信息,这 64位状态信息如下图所示:
在统计内存用量这个场景下,只用到了第63位,该位表示对应的页是否被该进程使用。因为/proc/<pid>下的文件都是特殊文件,mmap打开pagemap、smaps等文件会失败,并且errno会置ENODEV。所以只可以用read/fread打开,打开后使用lseek改变当前文件偏移量(cfo),然后read读取需要的字节数,再展开分析即可。
对比smaps文件,因为pagemap在生成的过程中不加锁,所以对频繁内存读写没有影响,但是一致性没有smaps强。
因为ESearch服务内部可以拿到磁盘文件内存映射返回的指针和长度,所以很方便能找到页表中对应的页。但如果不能拿到映射的地址范围,可以使用maps得到映射地址范围。
/proc/<pid>/maps文件描述了pid进程所有mmap打开文件或者开辟的内存段所在的地址区域,可以根据maps里的内容拿到感兴趣的地址段再进行操作。
2.5 系统命令
以下介绍的两种系统命令也能在一定程度上反应内核Page Cache或页表使用量的情况。
1. free –m会展示 Page Cache的大小。
2. vmstat也会展示Page Cache的大小,而且会展示PageCache中活跃页(active)和非活跃页(inactive)的量。
3.top会展示进程SHR内存占用,包括栈,大于128kb的内存分配,动态链接库,mmap打开的文件等。
4. sar 展示历史系统情况,-B参数展示页情况,其中pgfree展示系统中的空闲页数量,pgsteal表示从Page Cache中清理页的个数,在ESearch集群中该值和kswapd0进程的活跃度成正比。
三、总结
统计页表的工具有top、smaps、pagemap;统计文件占用PageCache的工具有mincore等;
文件映射下的内存统计主要分为两个方面:
1. 页表:可以使用smaps、top中的SHR、 pagemap来查看进程使用的内存;
2. Page Cache:可以使用free、vmstat、mincore系统调用来查看文件映射到物理内存页面所占用的内存。
进程层面和系统层面间的不同主要在于预读(read-ahead)等操作造成额外内存页会计算在Page Cache中,进程页表是不计算预读等操作所读入的内存页,页表用量只表示该进程需要页的数量。
四、参考
1. 内核版本:2.6;
2. 页表定位方式:参考《深入理解计算机系统》562页;
3. 基数树定位方式:参考https://en.wikipedia.org/wiki/Radix_tree。
相关推荐
- 使用nginx配置域名及禁止直接通过IP访问网站
-
前段时间刚搭建好这个网站,一直没有关注一个问题,那就是IP地址也可以访问我的网站,今天就专门研究了一下nginx配置问题,争取把这个问题研究透彻。1.nginx配置域名及禁止直接通过IP访问先来看n...
- 如何在 Linux 中使用 PID 号查找进程名称?
-
在Linux的复杂世界中,进程是系统运行的核心,每个进程都由一个唯一的「进程ID」(PID)标识。无论是系统管理员在排查失控进程,还是开发者在调试应用程序,知道如何将PID映射到对应的进程名称都是一项...
- Linux服务器硬件信息查询与日常运维命令总结
-
1.服务器硬件信息查询1.1CPU信息查询命令功能描述示例lscpu显示CPU架构、核心数、线程数等lscpucat/proc/cpuinfo详细CPU信息(型号、缓存、频率)cat/proc/c...
- Ubuntu 操作系统常用命令详解(ubuntu常用的50个命令)
-
UbuntuLinux是一款流行的开源操作系统,广泛应用于服务器、开发、学习等场景。命令行是Ubuntu的灵魂,也是高效、稳定管理系统的利器。本文按照各大常用领域,详细总结Ubuntu必学...
- 从 0 到 1:打造基于 Linux 的私有 API 网关平台
-
在当今微服务架构盛行的时代,API网关作为服务入口和安全屏障,其重要性日益凸显。你是否想过,不依赖商业方案,完全基于开源组件,在Linux上构建一个属于自己的私有API网关平台?今天就带你...
- Nginx搭建简单直播服务器(nginx 直播服务器搭建)
-
前言使用Nginx+Nginx-rtmp-module在Ubuntu中搭建简单的rtmp推流直播服务器。服务器环境Ubuntu16.04相关概念RTMP:RTMP协议是RealTi...
- Linux连不上网?远程卡?这篇网络管理指南你不能错过!
-
大家好!今天咱们聊个所有Linux用户都躲不开的“老大难”——网络管理。我猜你肯定遇到过这些崩溃时刻:新装的Linux系统连不上Wi-Fi,急得直拍桌子;远程服务器SSH连不上,提示“Connecti...
- 7天从0到上线!手把手教你用Python Flask打造爆款Web服务
-
一、为什么全网开发者都在疯学Flask?在当今Web开发的战场,Flask就像一把“瑞士军刀”——轻量级架构让新手3天速成,灵活扩展能力又能支撑百万级用户项目!对比Django的“重型装甲”,Flas...
- nginx配置文件详解(nginx反向代理配置详解)
-
Nginx是一个强大的免费开源的HTTP服务器和反向代理服务器。在Web开发项目中,nginx常用作为静态文件服务器处理静态文件,并负责将动态请求转发至应用服务器(如Django,Flask,et...
- 30 分钟搞定 Docker 安装与 Nginx 部署,轻松搭建高效 Web 服务
-
在云计算时代,利用容器技术快速部署应用已成为开发者必备技能。本文将手把手教你在阿里云轻量应用服务器上,通过Docker高效部署Nginx并发布静态网站,全程可视化操作,新手也能轻松上手!一、准...
- Nginx 配置实战:从摸鱼到部署,手把手教你搞定生产级配置
-
各位摸鱼搭子们!今天咱不聊代码里的NullPointerException,改聊点「摸鱼必备生存技能」——Nginx配置!先灵魂拷问一下:写了一堆接口却不会部署?服务器被恶意请求打崩过?静态资源加载...
- 如何使用 Daphne + Nginx + supervisor部署 Django
-
前言:从Django3.0开始支持ASGI应用程序运行,使Django完全具有异步功能。Django目前已经更新到5.0,对异步支持也越来越好。但是,异步功能将仅对在ASGI下运行的应用程序可用...
- Docker命令最全详解(39个最常用命令)
-
Docker是云原生的核心,也是大厂的必备技能,下面我就全面来详解Docker核心命令@mikechen本文作者:陈睿|mikechen文章来源:mikechen.cc一、Docker基本命令doc...
- ubuntu中如何查看是否已经安装了nginx
-
在Ubuntu系统中,可以通过以下几种方法检查是否已安装Nginx:方法1:使用dpkg命令(适用于Debian/Ubuntu)bashdpkg-l|grepnginx输出...
- OVN 概念与实践(德育概念的泛化在理论和实践中有什么弊端?)
-
今天我们来讲解OVN的概念和基础实践,要理解本篇博客的内容,需要前置学习:Linux网络设备-Bridge&VethPairLinux网络设备-Bridge详解OVS+Fa...
你 发表评论:
欢迎- 一周热门
-
-
UOS服务器操作系统防火墙设置(uos20关闭防火墙)
-
极空间如何无损移机,新Z4 Pro又有哪些升级?极空间Z4 Pro深度体验
-
手机如何设置与显示准确时间的详细指南
-
NAS:DS video/DS file/DS photo等群晖移动端APP远程访问的教程
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
如何修复用户配置文件服务在 WINDOWS 上登录失败的问题
-
一加手机与电脑互传文件的便捷方法FileDash
-
日本海上自卫队的军衔制度(日本海上自卫队的军衔制度是什么)
-
10个免费文件中转服务站,分享文件简单方便,你知道几个?
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
- 最近发表
-
- 使用nginx配置域名及禁止直接通过IP访问网站
- 如何在 Linux 中使用 PID 号查找进程名称?
- Linux服务器硬件信息查询与日常运维命令总结
- Ubuntu 操作系统常用命令详解(ubuntu常用的50个命令)
- 从 0 到 1:打造基于 Linux 的私有 API 网关平台
- Nginx搭建简单直播服务器(nginx 直播服务器搭建)
- Linux连不上网?远程卡?这篇网络管理指南你不能错过!
- 7天从0到上线!手把手教你用Python Flask打造爆款Web服务
- nginx配置文件详解(nginx反向代理配置详解)
- 30 分钟搞定 Docker 安装与 Nginx 部署,轻松搭建高效 Web 服务
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)