一、问题由来
我们使用Linux时,时常发现,cache占用大量内存。
如下面的示例,通过free命令查询内存情况,buff/cache已经占用了2.1G。Linux 2.4 开始, “buffer” 和 “cache” 已经被统一为页缓存,也就是cache整体是占用2.1G,而used才282M,导致了整体内存使用量很高。
[root@localhost ~]# free -h
total used free shared buff/cache available
Mem: 3.3G 282M 1.0G 40M 2.1G 2.8G
Swap: 3.0G 0B 3.0G
那我们如何能找出是由哪些进程和文件导致的cache?
二、Linux cache里的是什么?
首先,我们要了解 cache 是什么?
-
cache 是文件数据的页缓存,在使用MMap、缓冲 I/O(Buffered I/O)、预读取(Read-Ahead)等技术读、写文件时,内核会产生页缓存。当然,如果使用裸 I/O(Raw I/O)、**直接 I/O(Direct I/O)**可以绕过页缓存,直接在磁盘或分区上进行 I/O 操作。
-
从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。
-
从读的角度来说,不仅可以提高那些频繁访问数据的读取速度,也降低了频繁 I/O 对磁盘的压力。
三、查看Linux cache的工具
如何查看cache缓存了哪些文件内容?可以通过下面这些工具实现。
1 各种工具的功能对比
工具包含fincore、pcstat、hcache、vmtouch等,下面对这些工具做对比、功能介绍。
fincore | pcstat | hcache | vmtouch | |
---|---|---|---|---|
备注 | google出品。但是很久不维护了,不推荐使用 | 很多工具基于该工具二次开发 | 【推荐】基于pcstat二次开发 | 功能多 |
地址 | 非官方地址(无官方仓库): github.com/waleedmazha… | github.com/tobert/pcst… | github.com/silenceshel… | hoytech.com/vmtouch/ github.com/hoytech/vmt… |
查询文件/目录有多少被载入cache | ✓ | ✓ | ✓ | ✓ |
查询进程关联文件 cache | x | ✓ | ✓ | x |
统计文件cache总和 | x | x | ✓ | ✓ |
将文件载入cache | x | x | x | ✓ |
将文件cache锁住在内存 (不被换出到磁盘) |
x | x | x | ✓ |
将文件从cache中清除 | x | x | x | ✓ |
查询全局最大缓存文件 | x | x | ✓ | x |
docker内运行获取容器内文件cache | 未试验 | ✓ | ✓ | 容器内运行只能查宿主机的文件的cache |
fincore已经很久不维护了,下面不再讲解fincore,对其他工具将做详细说明。
2 特殊系统的定制工具
2.1 es-pcstat
为Elasticsearch专门定制的工具。参考:Elasticsearch 内存占用分析及 page cache 监控
四、pcstat
1 安装
安装pcstat有下面几种方式。
1.1 直接使用官方提供的可执行文件
官方提供了可执行文件可以直接下载,这样就不用自己编译了,如下所示:
if [ $(uname -m) == "x86_64" ] ; then
curl -L -o pcstat https://github.com/tobert/pcstat/raw/2014-05-02-01/pcstat.x86_64
else
curl -L -o pcstat https://github.com/tobert/pcstat/raw/2014-05-02-01/pcstat.x86_32
fi
但是,注意上面方式下载的文件是2014年编译的,有一些不足:
(1) 有一些 bug
(2)只针对部分操作系统和CPU架构可用
(3) 未包含当前最新的功能。
所以,推荐自己编译出可执行文件pcstat,该工具可以拷贝到各个机器上去执行。
1.2 手动编译、安装
按照下面步骤操作
1.如果没有安装go语言包,需要先安装go
2.clone源码
# clone 源码
git clone https://github.com/tobert/pcstat.git
3.编译pcstat源码
cd pcstat
# pcstat源工程是go语言的,所以需要build go工程
# go build 会自动下载相应的包或依赖包,但是访问golang.org网络不一定通,用VPN也不一定
# 所以可以指定用阿里云的golang镜像
go env -w GO111MODULE=on
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/
# build好后会有一个pcstat文件
go build
4.运行、验证
# 给pcstat文件赋予执行权限
chmod 755 pcstat
# 用执行pcstat验证一下
./pcstat /usr/local/bin/*
# 也可以复制到/usr/local/bin目录下,这样可在任何位置运行 pcstat
sudo cp -a pcstat /usr/local/bin
# 验证
pcstat /usr/local/bin/*
1.3 注意事项
1.3.1 不同操作系统、CPU架构的可执行文件不一样
例如,我自己搭建的VM的操作系统是linux
,CPU架构是arm64
,使用官方提供的”2014-05-02-01/pcstat.x86_64″,或者是linux\amd64的可执行文件,是无法执行的,错误如下:
./pcstat
-bash: ./pcstat: cannot execute binary file
所以,需要自己针对需要执行pcstat的目标系统,编译与目标系统操作系统、CPU架构匹配的可执行文件。
有哪些操作系统和CPU架构?
操作系统和CPU架构的枚举,可以参考go的文档:
var knownOS = map[string]bool{ // 操作系统
"darwin": true, // macOS
"ios": true,
"linux": true,
........
}
var knownArch = map[string]bool{ // CPU架构
"amd64": true,
"arm": true,
"arm64": true, // 注意是arm64,不是amd64
........
}
怎么知道运行的系统是什么操作系统,什么CPU架构?
可以使用go env的命令
go env GOOS GOARCH
linux
arm64
如何为其他操作系统、CPU架构的目标系统编译可执行文件?
我是linux/arm64的系统,能否为darwin/arm64系统编译可执行文件?
答案是可以的,go支持构建针对自己以外的操作系统和架构的 Go 二进制文件,这称为交叉编译。
使用方式:
# GOOS=OS GOARCH=ARCH go build
GOOS=darwin GOARCH=arm64 go build
2 使用
使用方式非常简单。
2.1 查询文件/目录有多少被载入缓存
命令是pcstat 指定的path
,path的几种示例/usr/*
、/usr/lib64/libdl-2.17.so
、/usr/**/**
。命令示例:
pcstat /usr/local/bin/*
+------------------------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|------------------------+----------------+------------+-----------+---------|
| /usr/local/bin/pcstat | 2542244 | 621 | 621 | 100.000 |
| /usr/local/bin/vmtouch | 120992 | 30 | 21 | 70.000 |
+------------------------+----------------+------------+-----------+---------+
2.2 指定pid查询指定进程对应的文件cache:
pcstat -pid 20908
+------------------------------------------------------------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|------------------------------------------------------------+----------------+------------+-----------+---------|
| /var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db | 32768 | 8 | 6 | 75.000 |
| /usr/lib64/libc-2.17.so | 1865776 | 456 | 456 | 100.000 |
| /usr/lib64/libdl-2.17.so | 73984 | 19 | 19 | 100.000 |
| /usr/lib64/libpthread-2.17.so | 184376 | 46 | 46 | 100.000 |
| /usr/lib64/ld-2.17.so | 160824 | 40 | 40 | 100.000 |
| /usr/bin/containerd | 38979464 | 9517 | 9517 | 100.000 |
+------------------------------------------------------------+----------------+------------+-----------+---------+
输出结果中的各列的意思如下:
-
Size
: 文件的总大小。这是文件在硬盘上的完整大小。 -
Pages
: 文件大小对应的内存页数。在大多数 Linux 系统中,一个内存页的大小是 4KB,所以这个数值是文件大小除以4KB的结果。 -
Cached
: 文件在内存中的缓存页数。这是当前被操作系统缓存的该文件的部分大小(以内存页为单位)。 -
Percent
: 文件被缓存的百分比。这个百分比是Cached
除以Pages
的结果
五、hcache
1 安装
安装方式有下面几种。
1.1 直接使用官方提供的可执行文件
官方提供了可执行文件可以直接下载,下载地址
注意:该包是针对linux,且应该是2020年之前编译的,所以有些功能不支持或者不完善,建议自己编译源码、安装。
1.2 手动编译、安装
步骤和hcache很类似,编译的一些注意事项可以参考上面pcstat部分。可按照下面步骤操作:
1.如果没有安装go语言包,需要先安装go,go 版本需要大于 1.12。
2.clone源码
# clone 源码
git clone https://github.com/silenceshell/hcache.git
3.编译hcache源码
cd hcache
# 源工程是go语言的,所以需要build go工程
# go build 会自动下载相应的包或依赖包,但是访问golang.org网络不一定通,用VPN也不一定
# 所以可以指定用阿里云的golang镜像
go env -w GO111MODULE=on
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/
# build好后会有一个hcache文件
go build
4.运行、验证
# 给hcache文件赋予执行权限
chmod 755 hcache
# 用执行hcache验证一下
./hcache /usr/local/bin/*
# 也可以复制到/usr/local/bin目录下,这样可在任何位置运行 hcache
sudo cp -a hcache /usr/local/bin
# 验证
hcache /usr/local/bin/*
2 使用
大部分功能继承于pcstat,其中有下面这些不同点。
2.1 sum功能
相比pcstat的结果,多了一行Sum,这个很有帮助,因为结果数据行比较多的时候,自己计算总和挺麻烦,而工具很贴心地提供了总和。
./hcache -pid 20618
+---------------------------------+----------------+-------------+----------------+-------------+---------+
| Name | Size │ Pages │ Cached Size │ Cached Pages│ Percent │
|---------------------------------+----------------+-------------+----------------+-------------+---------|
| /usr/lib64/libc-2.17.so | 1.779M | 456 | 1.779M | 456 | 100.000 |
| /usr/lib64/libpcre.so.1.2.0 | 259.742K | 65 | 259.742K | 65 | 100.000 |
| /usr/lib64/libselinux.so.1 | 200.172K | 51 | 200.172K | 51 | 100.000 |
| /usr/lib64/ld-2.17.so | 157.055K | 40 | 157.055K | 40 | 100.000 |
|---------------------------------+----------------+-------------+----------------+-------------+---------|
│ Sum │ 2.857M │ 738 │ 2.857M │ 738 │ 100.000 │
2.2 查询全局top【结果不准确】
注意:该功能的初衷是挺好的,但是实际验证发现不准确,例如某一个文件被cached 1G,但是sudo ./hcache –top结果最高的可能才100M。
使用方式:sudo ./hcache –top xxx
sudo ./hcache --top 3
+--------------------------------+----------------+-------------+----------------+-------------+---------+
| Name | Size │ Pages │ Cached Size │ Cached Pages│ Percent │
|--------------------------------+----------------+-------------+----------------+-------------+---------|
| /usr/lib/locale/locale-archive | 101.259M | 25923 | 101.259M | 25923 | 100.000 |
| /usr/bin/dockerd | 66.375M | 16993 | 66.375M | 16993 | 100.000 |
| /usr/bin/containerd | 37.174M | 9517 | 37.174M | 9517 | 100.000 |
|--------------------------------+----------------+-------------+----------------+-------------+---------|
│ Sum │ 204.808M │ 52433 │ 204.808M │ 52433 │ 100.000 │
六、vmtouch
该工具是用C语言编写的,用于了解和控制Unix和类Unix系统的文件系统缓存的工具。
1 安装
安装方式有下面几种。
1.1 直接使用官方提供的可执行文件
可惜没有找到官方编译好的可执行文件,所以需要自己安装。
1.2 手动编译、安装
1.clone源码
# clone 源码
git clone https://github.com/silenceshell/hcache.git
2.编译hcache源码
cd vmtouch
make
# install后可执行文件自动复制到/usr/local/bin目录下,这样可在任何位置运行 vmtouch
sudo make install
3.运行、验证
vmtouch
2 使用
2.1 查询文件/目录有多少被载入cache
功能和执行结果和pcstat类似,-v参数表示展示详情
vmtouch -v /usr/local/bin/
/usr/local/bin/hcache
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 644/644
/usr/local/bin/pcstat
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 621/621
/usr/local/bin/vmtouch
[OOOOOO OOOOOOOOOOOOOOO] 21/30
Files: 3
Directories: 0
Resident Pages: 1286/1295 5M/5M 99.3%
Elapsed: 0.000312 seconds
2.2 将文件/目录载入cache
该方式可以预热文件的加载,提高后续读取的速度。
vmtouch -vt app.log
app.log
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 26876/26876
Files: 1
Directories: 0
Touched Pages: 26876 (104M)
Elapsed: 0.064051 seconds
2.3 将文件/目录从cache中清除
不想让某些文件占用cache,可以将其从cache清除。
vmtouch -ve app.log
Evicting app.log
Files: 1
Directories: 0
Evicted Pages: 26876 (104M)
Elapsed: 0.008625 seconds
2.4 将文件cache锁住在内存不被换出到磁盘
可以锁定文件或目录的所有页在页缓存中,防止它们被系统的内存管理策略驱逐。
# -d是后台模式,这样vmtouch命令将在后台执行,持续锁定内存
vmtouch -dl app.log
如果要解除锁定,需要杀掉vmtouch后台进程
ps -ef|grep vmtouch
root 3250 1 0 03:59 ? 00:00:00 vmtouch -dl app.log
kill 3250
六、查看文件被哪些进程占用
通过上面的命令,我们知道了哪些文件占用较多cache。下面就可以通过lsof命令查看文件被哪些使用了,然后再去分析进程是如何在操作文件的。
1 lsof查看文件被进程占用情况
lsof /root/logfile/app.log
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
less 4964 root 4r REG 253,0 110081812 17563958 app.log
tail 5184 root 3r REG 253,0 110081812 17563958 app.log
七、小结
以上对查询cache的工具做了介绍:
- 通过pcstat、hcache、vmtouch等工具可以查看哪些文件被加载到了cache,占用多少,以及进程关联的文件cache,通过lsof。
- 通过lsof命令查看文件被哪些使用了。
下一篇将分析这些工具的原理,目的是学习一下linux系统的一些底层知识,更方便以后分析问题、以及可以根据需求做二次开发。