极客时间已完结课程限时免费阅读

22 | 答疑(三):文件系统与磁盘的区别是什么?

22 | 答疑(三):文件系统与磁盘的区别是什么?-极客时间

22 | 答疑(三):文件系统与磁盘的区别是什么?

讲述:冯永吉

时长11:01大小10.10M

你好,我是倪朋飞。
专栏更新至今,四大基础模块的第二个模块——内存性能篇,我们就已经学完了。很开心你还没有掉队,仍然在积极学习和实践操作,并且热情地留言与讨论。
这些留言中,我非常高兴看到,很多同学用学过的案例思路,解决了实际工作中的性能问题。我也非常感谢 espzest、大甜菜、Smile 等积极思考的同学,指出了文章中某些不当或者不严谨的地方。另外,还有我来也、JohnT3e、白华等同学,积极在留言区讨论学习和实践中的问题,也分享了宝贵的经验,在这里也非常感谢你们。
今天是性能优化的第三期。照例,我从内存模块的留言中摘出了一些典型问题,作为今天的答疑内容,集中回复。为了便于你学习理解,它们并不是严格按照文章顺序排列的。
每个问题,我都附上了留言区提问的截屏。如果你需要回顾内容原文,可以扫描每个问题右下方的二维码查看。

问题 1:内存回收与 OOM

虎虎的这个问题,实际上包括四个子问题,即,
怎么理解 LRU 内存回收?
回收后的内存又到哪里去了?
OOM 是按照虚拟内存还是实际内存来打分?
怎么估计应用程序的最小内存?
其实在 Linux 内存的原理篇Swap 原理篇中我曾经讲到,一旦发现内存紧张,系统会通过三种方式回收内存。我们来复习一下,这三种方式分别是 :
基于 LRU(Least Recently Used)算法,回收缓存;
基于 Swap 机制,回收不常访问的匿名页;
基于 OOM(Out of Memory)机制,杀掉占用大量内存的进程。
前两种方式,缓存回收和 Swap 回收,实际上都是基于 LRU 算法,也就是优先回收不常访问的内存。LRU 回收算法,实际上维护着 active 和 inactive 两个双向链表,其中:
active 记录活跃的内存页;
inactive 记录非活跃的内存页。
越接近链表尾部,就表示内存页越不常访问。这样,在回收内存时,系统就可以根据活跃程度,优先回收不活跃的内存。
活跃和非活跃的内存页,按照类型的不同,又分别分为文件页和匿名页,对应着缓存回收和 Swap 回收。
当然,你可以从 /proc/meminfo 中,查询它们的大小,比如:
# grep表示只保留包含active的指标(忽略大小写)
# sort表示按照字母顺序排序
$ cat /proc/meminfo | grep -i active | sort
Active(anon): 167976 kB
Active(file): 971488 kB
Active: 1139464 kB
Inactive(anon): 720 kB
Inactive(file): 2109536 kB
Inactive: 2110256 kB
第三种方式,OOM 机制按照 oom_score 给进程排序。oom_score 越大,进程就越容易被系统杀死。
当系统发现内存不足以分配新的内存请求时,就会尝试直接内存回收。这种情况下,如果回收完文件页和匿名页后,内存够用了,当然皆大欢喜,把回收回来的内存分配给进程就可以了。但如果内存还是不足,OOM 就要登场了。
OOM 发生时,你可以在 dmesg 中看到 Out of memory 的信息,从而知道是哪些进程被 OOM 杀死了。比如,你可以执行下面的命令,查询 OOM 日志:
$ dmesg | grep -i "Out of memory"
Out of memory: Kill process 9329 (java) score 321 or sacrifice child
当然了,如果你不希望应用程序被 OOM 杀死,可以调整进程的 oom_score_adj,减小 OOM 分值,进而降低被杀死的概率。或者,你还可以开启内存的 overcommit,允许进程申请超过物理内存的虚拟内存(这儿实际上假设的是,进程不会用光申请到的虚拟内存)。
这三种方式,我们就复习完了。接下来,我们回到开始的四个问题,相信你自己已经有了答案。
LRU 算法的原理刚才已经提到了,这里不再重复。
内存回收后,会被重新放到未使用内存中。这样,新的进程就可以请求、使用它们。
OOM 触发的时机基于虚拟内存。换句话说,进程在申请内存时,如果申请的虚拟内存加上服务器实际已用的内存之和,比总的物理内存还大,就会触发 OOM。
要确定一个进程或者容器的最小内存,最简单的方法就是让它运行起来,再通过 ps 或者 smap ,查看它的内存使用情况。不过要注意,进程刚启动时,可能还没开始处理实际业务,一旦开始处理实际业务,就会占用更多内存。所以,要记得给内存留一定的余量。

问题 2: 文件系统与磁盘的区别

文件系统和磁盘的原理,我将在下一个模块中讲解,它们跟内存的关系也十分密切。不过,在学习 Buffer 和 Cache 的原理时,我曾提到,Buffer 用于磁盘,而 Cache 用于文件。因此,有不少同学困惑了,比如 JJ 留言中的这两个问题。
读写文件最终也是读写磁盘,到底要怎么区分,是读写文件还是读写磁盘呢?
读写磁盘难道可以不经过文件系统吗?
如果你也有相同的疑问,主要还是没搞清楚,磁盘和文件的区别。我在“怎么理解内存中的 Buffer 和 Cache”文章的留言区简单回复过,不过担心有同学没有看到,所以在这里重新讲一下。
磁盘是一个存储设备(确切地说是块设备),可以被划分为不同的磁盘分区。而在磁盘或者磁盘分区上,还可以再创建文件系统,并挂载到系统的某个目录中。这样,系统就可以通过这个挂载目录,来读写文件。
换句话说,磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储。
你在很多地方都会看到这句话, Linux 中一切皆文件。换句话说,你可以通过相同的文件接口,来访问磁盘和文件(比如 open、read、write、close 等)。
我们通常说的“文件”,其实是指普通文件。
而磁盘或者分区,则是指块设备文件。
你可以执行 “ls -l < 路径 >” 查看它们的区别。如果不懂 ls 输出的含义,别忘了 man 一下就可以。执行 man ls 命令,以及 info ‘(coreutils) ls invocation’ 命令,就可以查到了。
在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的“裸 I/O”。
这两种读写方式使用的缓存自然不同。文件系统管理的缓存,其实就是 Cache 的一部分。而裸磁盘的缓存,用的正是 Buffer。
更多关于文件系统、磁盘以及 I/O 的原理,你先不要着急,往后我们都会讲到。

问题 3: 如何统计所有进程的物理内存使用量

这其实是 怎么理解内存中的 Buffer 和 Cache 的课后思考题,无名老卒、Griffin、JohnT3e 等少数几个同学,都给出了一些思路。
比如,无名老卒同学的方法,是把所有进程的 RSS 全部累加:
这种方法,实际上导致不少地方会被重复计算。RSS 表示常驻内存,把进程用到的共享内存也算了进去。所以,直接累加会导致共享内存被重复计算,不能得到准确的答案。
留言中好几个同学的答案都有类似问题。你可以重新检查一下自己的方法,弄清楚每个指标的定义和原理,防止重复计算。
当然,也有同学的思路非常正确,比如 JohnT3e 提到的,这个问题的关键在于理解 PSS 的含义。
你当然可以通过 stackexchange 上的链接找到答案,不过,我还是更推荐,直接查 proc 文件系统的文档
The “proportional set size” (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.
这里我简单解释一下,每个进程的 PSS ,是指把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和。
就像文档中的这个例子,一个进程的非共享内存为 1000 页,它和另一个进程的共享进程也是 1000 页,那么它的 PSS=1000/2+1000=1500 页。
这样,你就可以直接累加 PSS ,不用担心共享内存重复计算的问题了。
比如,你可以运行下面的命令来计算:
# 使用grep查找Pss指标后,再用awk计算累加值
$ grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kB\n", total }'
391266 kB

问题 4: CentOS 系统中如何安装 bcc-tools

很多同学留言说用的是 CentOS 系统。虽然我在文章中也给出了一个参考文档,不过 bcc-tools 工具安装起来还是有些困难。
比如白华同学留言表示,网络上的教程不太完整,步骤有些乱:
不过,白华和渡渡鸟 _linux 同学在探索实践后,留言分享了他们的经验,感谢你们的分享。
在这里,我也统一回复一下,在 CentOS 中安装 bcc-tools 的步骤。以 CentOS 7 为例,整个安装主要可以分两步。
第一步,升级内核。你可以运行下面的命令来操作:
# 升级系统
yum update -y
# 安装ELRepo
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
# 安装新内核
yum remove -y kernel-headers kernel-tools kernel-tools-libs
yum --enablerepo="elrepo-kernel" install -y kernel-ml kernel-ml-devel kernel-ml-headers kernel-ml-tools kernel-ml-tools-libs kernel-ml-tools-libs-devel
# 更新Grub后重启
grub2-mkconfig -o /boot/grub2/grub.cfg
grub2-set-default 0
reboot
# 重启后确认内核版本已升级为4.20.0-1.el7.elrepo.x86_64
uname -r
第二步,安装 bcc-tools:
# 安装bcc-tools
yum install -y bcc-tools
# 配置PATH路径
export PATH=$PATH:/usr/share/bcc/tools
# 验证安装成功
cachestat

问题 5: 内存泄漏案例的优化方法

这是我在 内存泄漏了,我该如何定位和处理 中留的一个思考题。这个问题是这样的:
在内存泄漏案例的最后,我们通过增加 free() 调用,释放了函数 fibonacci() 分配的内存,修复了内存泄漏的问题。就这个案例而言,还有没有其他更好的修复方法呢?
很多同学留言写下了自己的想法,都很不错。这里,我重点表扬下郭江伟同学,他给出的方法非常好:
他的思路是不用动态内存分配的方法,而是用数组来暂存计算结果。这样就可以由系统自动管理这些栈内存,也不存在内存泄漏的问题了。
这种减少动态内存分配的思路,除了可以解决内存泄漏问题,其实也是常用的内存优化方法。比如,在需要大量内存的场景中,你就可以考虑用栈内存、内存池、HugePage 等方法,来优化内存的分配和管理。
除了这五个问题,还有一点我也想说一下。很多同学在说工具的版本问题,的确,生产环境中的 Linux 版本往往都比较低,导致很多新工具不能在生产环境中直接使用。
不过,这并不代表我们就无能为力了。毕竟,系统的原理都是大同小异的。这其实也是我一直强调的观点。
在学习时,最好先用最新的系统和工具,它们可以为你提供更简单直观的结果,帮你更好的理解系统的原理。
在你掌握了这些原理后,回过头来,再去理解旧版本系统中的工具和原理,你会发现,即便旧版本中的很多工具并不是那么好用,但是原理和指标是类似的,你依然可以轻松掌握它们的使用方法。
最后,欢迎继续在留言区写下你的疑问,我会持续不断地解答。我的目的不变,希望可以和你一起,把文章的知识变成你的能力,我们不仅仅在实战中演练,也要在交流中进步。
分享给需要的人,Ta购买本课程,你将得20
生成海报并分享

赞 27

提建议

上一篇
21 | 套路篇:如何“快准狠”找到系统内存的问题?
下一篇
23 | 基础篇:Linux 文件系统是怎么工作的?
unpreview
 写留言

精选留言(44)

  • 白华
    2019-01-09
    自己跑虚拟机跑的k8s小集群,node节点跑的镜像太多,就特别卡,看集群情况发现好多pod都死了,看虚拟机上面就写到oom自动杀进程了,以前从没遇到过oom,这次一下就知道怎么回事了

    作者回复: 😊 k8s别忘了设置好内存资源的请求和限制

    共 2 条评论
    15
  • wykkx
    2019-01-12
    老师您好,请教一个问题,您说匿名页回收是使用swap机制,那么这里有几个问题:一是如果我的系统不开启swap(现在很多系统都是不开启swap的,尤其是web类应用,为了提升响应时间),那么匿名页还怎么回收?二是即使我开启了swap,匿名页被放到了swap上,那么swap是如何清理这些匿名页的,总不能一致保存这些匿名页吧。谢谢老师 2019-01-10  作者回复 1. 不开swap,没法回收 2. 取决于这些页的实际访问情况,只要一访问,就会换入到内存中 ---------追问------------------------- 首先谢谢老师的问答,可能是我没有描述清楚。一是,如果按照老师说的“不开swap,没法回收”那么我系统产生的大量匿名页怎么办?是不是一直在内存里,不能被回收?直到引发oom,把这个进程干掉?;二是“ 取决于这些页的实际访问情况,只要一访问,就会换入到内存中”如果这些被换到swap上的匿名页量很大 ,把swap都写满了,这个时候是怎么选择从swap里清理哪些匿名页呢?
    展开

    作者回复: 1.是的;2. swap都满了,说明内存也耗尽了呀,这时候就要OOM了

    共 2 条评论
    13
  • 陳先森
    2019-04-05
    很多同学装bcc工具的时候都提示找不到动态库之类的,经过本人亲测是因为没有安装匹配新内核版本的内核头包。我的系统centos7.3更新了之后内核是5.0.5版本的,升完之后一直提示缺少库文件,我的做法是 rpm -qa |grep kerner,先查找系统内核版本,网上查找相应的匹配kerner-devel包,及时没有相同版本也不要紧,比如5.0.5-1的版本就是我系统的版本网上找了没有这个版本对应的kerner-devel包但是不知道为什么升级了会安装这个版本,我系统升级前centos7.3,升级后7.6。我就下载了5.0.5-3并安装,然后版本的,找到对应的包版本,rpm -ql 包版本,做个软连接就OK。至此bcc已经安装完成。做为一个linux运维我都装了2天,可想而知大家。此方法亲测centos7.3
    展开

    作者回复: 👍谢谢分享

    共 3 条评论
    7
  • 好好学习
    2019-01-13
    内存统计这样也可以吧smem|awk '{total+=$7};END{printf "%d kb/n",total}'

    作者回复: smem是可以的👍,不过要注意计算 PSS 而不是 RSS

    8
  • 莫名
    2020-04-10
    OOM触发的时机基于虚拟内存。不赞同这一说法,OOM是在内核页中断并尝试分配物理页工程中触发的,主要依据rss进行判断。 举个简单例子:容器限制100M,app一次性malloc 500M,相当于申请500M虚拟内存,但不会触发OOM,然后逐字节写入,不断触发页中断分配物理内存,当分配的物理内存达到100M时触发OOM。
    展开
    3
  • yann [扬] :曹同学
    2020-03-13
    centos 安装方法安装完了,cachestat可以,memleak 不能使用,bpf text报错
    共 1 条评论
    3
  • J.Smile
    2020-08-11
    小结: ① 内存紧张时的回收三方式:LRU+Swap+OOM ② 内存页根据类型分为:文件页(对应缓存回收)和匿名页(对应swap回收)。 ③OOM方式内存回收 -- 如果回收完文件页和匿名页后,内存够用了,皆大欢喜,把回收回来的内存分配给进程就可以了。但如果内存还是不足,OOM 就要起作用了。 -- OOM 触发的时机基于虚拟内存。换句话说,进程在申请内存时,如果申请的虚拟内存加上服务器实际已用的内存之和,比总的物理内存还大,就会触发 OOM。 ④磁盘与文件的区别和联系 - 磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储。 - 在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的“裸 I/O”。 - 文件系统管理的缓存,其实就是 Cache 的一部分。而裸磁盘的缓存,用的正是 Buffer。
    展开
    2
  • 飘云
    2020-02-09
    老师 ,一个进程的 oom score 到底是根据什么计算的并没有说, 或者没有说清楚,说明白
    2
  • 平安喜乐
    2019-03-12
    打卡: bcc-tools install(centos6.9) 已尝试可以顺利安装 https://blog.csdn.net/luckgl/article/details/88355074

    作者回复: 谢谢分享

    共 2 条评论
    2
  • ninuxer
    2019-01-09
    打卡day23 喜欢一篇文章看两次,第一次看,第二次是实践的时候再看一遍

    作者回复: 👍

    2
  • linker
    2021-12-15
    大佬,关于oom的出发时机,我这边做了实验测试,malloc分配失败是返回NULL,并且设置errno=12(内存不足)。但是不会出发oom. 只有当read / write调用时,do_page_fault映射不到页表才出发oom
    1
  • Geek_f702be
    2020-11-06
    上一节提到常驻内存是进程实际使用的物理内存,不过,它不包括 Swap 和共享内存。 这一节说RSS 表示常驻内存,把进程用到的共享内存也算了进去。所以,直接累加会导致共享内存被重复计算,不能得到准确的答案 这是不是前后矛盾了
    1
  • cuikt
    2019-05-06
    bcc-tool 确实厉害,无奈生成环境不可能轻易升级内核,只能学习使用。
    1
  • H&HH
    2019-03-22
    老师,初始系统版本是centos7.3,按照你给的安装bcc-tools步骤安装完后还是出现了以下报错,请问老师怎么回事呀 [root@server tools]# ./cachetop Traceback (most recent call last): File "./cachetop", line 21, in <module> from bcc import BPF File "/usr/lib/python2.7/site-packages/bcc/__init__.py", line 27, in <module> from .libbcc import lib, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE File "/usr/lib/python2.7/site-packages/bcc/libbcc.py", line 17, in <module> lib = ct.CDLL("libbcc.so.0", use_errno=True) File "/usr/lib64/python2.7/ctypes/__init__.py", line 360, in __init__ self._handle = _dlopen(self._name, mode) OSError: /opt/lampp/lib/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by /lib64/libbcc.so.0) [root@server tools]# uname -r 5.0.3-1.el7.elrepo.x86_64 [root@server tools]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core)
    展开

    作者回复: 看错误是找不到依赖的动态链接库,可以试试修复安装 libstdc++

    共 2 条评论
    1
  • 划时代
    2019-01-09
    老师,在“如何统计所有进程的物理内存使用量”的问题中,我曾尝试将/proc/[pid]/smaps中的项Pss相加,但发现一个进程中有多个地址段的Pss为0kB,而Rss是不为0kB的,该区域占用了物理内存却没有计算在Pss内,表示不太理解?

    作者回复: 什么进程?

    1
  • Geek_b3b8da
    2022-12-07 来自浙江
    我终于弄明白buff和cache了,如果程序的数据是写在挂载点上边的,那么就会经过文件系统流程是vfs---文件系统---通用层---设备层使用cache来缓存读和写,如果类型dd这样的程序写明是跳过文件系统直接和磁盘交互流程是通用层----设备层,那么就是buff来缓存数据
  • 秋玉白狼
    2022-10-21 来自浙江
    倪老师 你好 我是一名运维工程师,通过看你的这个技术专栏 让我在了解系统运行机制和对性能问题排除解决方面都有了很大的提高!真心感谢!
  • Geek_982ffd
    2022-03-02
    关于bcc工具的安装 仅4.1内核及以上支持 更进一步建议内核版本4.6及以上版本 Centos7源码安装:https://github.com/iovisor/bcc/blob/master/INSTALL.md#centos---source 其他发行版安装也可以在该链接中找到。 最重要的是 文中出现的bcc包github链接出现了 在编译安装时有 头文件不全的提示 强烈建议选择该链接下载最新版本bcc 0.24 : https://github.com/iovisor/bcc/releases (兼容最新内核版本5.16)避免此类错误。
  • lfn
    2021-12-05
    2021-12-05打卡。
  • 宏典
    2021-09-22
    问题 5: 内存泄漏案例的优化方法 中,将堆改成栈。在函数返回的时候栈空间的局部变量不是释放了吗?