【Linux性能】Linux cache占用大量内存,如何分析是哪些进程、文件导致?

一、问题由来

我们使用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的文档:

github.com/golang/go/b…

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系统的一些底层知识,更方便以后分析问题、以及可以根据需求做二次开发。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYzH0Wyc' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片