earlyoom - 早期 OOM 守护进程
oom-killer 在 Linux 用户中通常名声不佳。这可能部分解释了为什么 Linux 只在别无选择时才会调用它。 它会先交换出桌面环境,丢弃整个页面缓存并清空所有缓冲区,最终才会杀死一个进程。至少我认为它会这样做。 我还没有足够的耐心坐在无响应的系统前等待它发生。
这让我和其他人开始怀疑 oom-killer 是否可以配置为更早介入:reddit r/linux、superuser.com、unix.stackexchange.com。
事实证明,不行。至少使用内核中的 oom-killer 时不行。 然而,在用户空间中,我们可以为所欲为。
earlyoom 旨在简单且稳固。它用纯 C 编写,没有依赖项。 一个广泛的测试套件(单元测试和集成测试)是用 Go 编写的。
它做什么
earlyoom 每秒最多检查 10 次可用内存和空闲交换空间(如果空闲内存很多,则检查频率更低)。
默认情况下,如果两者都低于 10%,它将杀死最大的进程(最高的 oom_score)。
百分比值可以通过命令行参数进行配置。
在下面的 free -m 输出中,可用内存为 2170 MiB,
空闲交换空间为 231 MiB。
总计 已用 空闲 共享 缓冲/缓存 可用 内存: 7842 4523 137 841 3182 2170 交换: 1023 792 231
为什么检查“可用”内存而不是“空闲”内存? 在健康的 Linux 系统上,“空闲”内存应该接近于零, 因为 Linux 会使用所有可用的物理内存来缓存磁盘访问。 这些缓存可以在内存被其他用途需要时随时被丢弃。
“可用”内存对此进行了计算。它汇总了所有未使用或可以立即释放的内存。
请注意,你需要较新版本的
free 和 Linux 内核 3.14+ 才能看到“可用”列。如果你拥有
较新的内核,但旧版本的 free,你可以从
grep MemAvailable /proc/meminfo 中获取该值。
当你的可用内存和空闲交换空间都低于用户空间进程可用总内存(=total-shared)的 10% 时,
它会向内核认为使用内存最多的进程发送 SIGTERM 信号(/proc/*/oom_score)。
另请参阅
- nohang,一个类似于 earlyoom 的项目, 使用 Python 编写,并带有额外的功能和配置选项。
- Facebook 的压力停滞信息(psi)内核补丁 以及配套的用户空间辅助工具 oomd。 这些补丁已合并到 Linux 4.20 中。
为什么不触发内核 oom killer?
你可以通过传递 --kernel-oom 标志来让 earlyoom 触发内核 oom killer(echo f > /proc/sysrq-trigger)。但请注意以下几点:
在某些 Linux 内核版本(已在 v4.0.5 上测试)中,手动触发内核 oom killer 完全不起作用。 也就是说,它可能只会释放一些图形 内存(这些内存会立即被重新分配),而实际上并没有杀死 任何进程。在这里 你 可以看到在我的机器上(Intel 集成显卡)这种情况的表现。
此问题已在 Linux v5.17 (commit f530243a) 中修复。
与 Linux 内核的默认行为一样,
earlyoom 通过读取 /proc/*/oom_score 来寻找其目标进程。
earlyoom 使用多少内存?
大约 2 MiB(VmRSS),但其中只有 220 kiB 是私有内存(RssAnon)。
其余部分是 libc 库(RssFile),与其他进程共享。
所有内存都使用 mlockall() 锁定,以确保 earlyoom 在低内存情况下不会变慢。
下载和编译
自行编译很简单:
git clone https://github.com/rfjakob/earlyoom.git
cd earlyoom
make
可选:运行集成自检:
make test
通过将其注册为服务来自动启动 earlyoom:
sudo make install # systemd
sudo make install-initscript # non-systemd
请注意,对于禁用了 SELinux 的系统(Ubuntu 19.04、Debian 9 ...),可以安全地忽略报告设置上下文失败的 chcon 警告。
对于 Debian 10+ 和 Ubuntu 18.04+,有一个 Debian 软件包:
sudo apt install earlyoom
对于带有 EPEL 的 Fedora 和 RHEL 8,有一个 Fedora 软件包:
sudo dnf install earlyoom
sudo systemctl enable --now earlyoom
对于 Arch Linux,有一个 Arch Linux 软件包:
sudo pacman -S earlyoom
sudo systemctl enable --now earlyoom
其他发行版的可用性:参见 repology 页面。
使用
只需启动您刚刚编译的可执行文件:
./earlyoom
它会告知你拥有多少内存和交换空间,最小值是多少, 可用内存有多少,以及空闲交换空间有多少。
./earlyoom
eearlyoom v1.8
mem total: 23890 MiB, user mem total: 21701 MiB, swap total: 8191 MiB
sending SIGTERM when mem avail <= 10.00% and swap free <= 10.00%,
SIGKILL when mem avail <= 5.00% and swap free <= 5.00%
mem avail: 20012 of 21701 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
mem avail: 20031 of 21721 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
mem avail: 20033 of 21723 MiB (92.22%), swap free: 5251 of 8191 MiB (64.11%)
[...]
如果数值低于最小值,进程将被终止,直到其再次高于最小值。每个操作都会记录到 stderr。如果你正在以 systemd 服务的方式运行 earlyoom,你可以使用
systemctl status earlyoom
测试
为了查看 earlyoom 的实际效果,请创建/模拟一个内存泄漏,并让 earlyoom 执行其功能:
tail /dev/zero
检查日志
如果在进程被 earlyoom 终止后需要执行其他操作(例如发送邮件),您可以通过以下方式解析日志:
sudo journalctl -u earlyoom | grep sending
上述测试命令(tail /dev/zero)的示例输出如下:
Feb 20 10:59:34 debian earlyoom[10231]: sending SIGTERM to process 7378 uid 1000 "tail": oom_score 156, VmRSS 4962 MiB
对于
earlyoom的较旧版本,请使用:sudo journalctl -u earlyoom | grep -iE "(sending|killing)"
通知
从 1.6 版本开始,earlyoom 可以通过系统 d-bus 发送关于被终止进程的通知。传入 -n 以启用它们。
要在 GUI 会话中实际看到这些通知,你需要以用户身份运行 systembus-notify 。
此外,earlyoom 可以为每个被终止的进程执行一个脚本,并通过 EARLYOOM_PID、EARLYOOM_UID 和
EARLYOOM_NAME 环境变量提供有关该进程的信息。传入 -N /path/to/script 以在进程被终止后启用
,或传入 -P /path/to/script 以在终止前调用。
警告:在 dryrun 模式下,脚本将快速连续执行,请确保你实现了某种形式的速率限制。
优先进程
命令行标志 --prefer 指定优先终止的进程;
同样,--avoid 指定
避免终止的进程。详情请参阅 https://github.com/rfjakob/earlyoom/blob/master/MANPAGE.md#--prefer-regex。
配置文件
如果你将 earlyoom 作为系统服务运行(通过 systemd 或 init.d),你可以通过 /etc/default/earlyoom 中提供的文件来调整其配置。该文件的注释中已包含一些示例,你可以基于支持的命令行选项,利用这些示例来构建自己的配置集,例如:
EARLYOOM_ARGS="-m 5 -r 60 --avoid '(^|/)(init|Xorg|ssh)$' --prefer '(^|/)(java|chromium)$'"
调整文件后,只需重启服务即可应用更改。例如,对于 systemd:
systemctl restart earlyoom
请注意,此配置文件对 systemd/init.d 之外的 earlyoom 实例没有影响。
命令行选项
earlyoom v1.8
Usage: ./earlyoom [OPTION]...
-m PERCENT[,KILL_PERCENT] set available memory minimum to PERCENT of total
(default 10 %).
earlyoom sends SIGTERM once below PERCENT, then
SIGKILL once below KILL_PERCENT (default PERCENT/2).
-s PERCENT[,KILL_PERCENT] set free swap minimum to PERCENT of total (default
10 %).
Note: both memory and swap must be below minimum for
earlyoom to act.
-M SIZE[,KILL_SIZE] set available memory minimum to SIZE KiB
-S SIZE[,KILL_SIZE] set free swap minimum to SIZE KiB
-n enable d-bus notifications
-N /PATH/TO/SCRIPT call script after oom kill
-P /PATH/TO/SCRIPT call script before oom kill
-g kill all processes within a process group
-d, --debug enable debugging messages
-v print version information and exit
-r INTERVAL memory report interval in seconds (default 1), set
to 0 to disable completely
-p set niceness of earlyoom to -20 and oom_score_adj to
-100
--ignore-root-user do not kill processes owned by root
--sort-by-rss find process with the largest rss (default oom_score)
--prefer REGEX prefer to kill processes matching REGEX
--avoid REGEX avoid killing processes matching REGEX
--ignore REGEX ignore processes matching REGEX
--dryrun dry run (do not kill any processes)
--syslog use syslog instead of std streams
-h, --help this help text
有关详细信息,请参阅 man page。
贡献
欢迎通过 github 提交 bug 报告和 pull request。特别是,我很乐意 接受
- 使用案例报告和反馈
实现说明
- 我们未使用 procps/libproc2,因为 procps_pids_select() 出于某种原因总是解析 /proc/$pid/status。 这相对昂贵,而且我们并不需要它。
变更日志
-
v1.9.0, 2025-09-16
- 添加
-P /path/to/script选项,用于在终止某个进程之前执行 (#348) - 为
-n和-N增加更多调试日志 - 在终止某个进程时也记录 oom_score_adj
- 添加
-
v1.8.2, 2024-05-07
-
v1.8.1, 2024-04-17
- 修复由消息措辞更改引起的琐碎测试失败 (commit)
-
v1.8, 2024-04-15
- 引入
user mem total/meminfo_t.UserMemTotal并基于其计算 MemAvailablePercent (commit, man page 中的更多信息) - 使用
process_mrelease(#266) - 支持
NO_COLOR(https://no-color.org/) - 不再被具有僵尸主线程的进程所混淆 (commit)
- 添加
--sort-by-rss,感谢 @RanHuang!这将选择具有最大 RSS 的进程进行终止, 而不是具有最大 oom_score 的进程。 - Gitlab CI 测试套件现在也在 Amazon Linux 2 和 Oracle Linux 7 上运行。
- 引入
-
v1.7, 2022-03-05
- 添加
-N标志,以便在每次终止进程时运行脚本 (commit, man page 章节) - 添加
-g标志以终止整个进程组 (#247) - 移除
-i标志(为兼容性而忽略),因为它在 Linux 内核 5.9+ 上 无法正常工作 (#234) - 加固:在启动时丢弃环境能力 (#234)
- 添加
-
v1.6.2, 2020-10-14
-
1.6.1, 2020-07-07
-
1.6, 2020-04-11
- 用
dbus-send/ systembus-notify (#183) 替换旧的notify-sendGUI 通知逻辑-n/-N现在启用新逻辑- 你需要在 GUI 会话中运行 systembus-notify 以便工作通知生效
- 优雅处理以
hidepid
挂载的
/proc(issue #184)
- 用
-
v1.5, 2020-03-22
-
v1.4, 2020-03-01
- 通过延迟加载进程属性使受害者选择逻辑快 50%
- 除 pid 和名称外,记录被杀死进程的
uid用户 ID - 将调试日志颜色设为浅灰色
- 代码清理
- 尽可能使用块局部变量
- 引入 PATH_LEN 以替换多个硬编码的缓冲区长度
- 扩展测试套件 (
make test) - 在可用时运行
cppcheck - 添加单元测试基准 (
make bench) - 在 systemd 单元文件
earlyoom.service中放弃 root 权限
-
v1.3.1, 2020-02-27
- 修复在大量 RAM 系统上的虚假测试套件失败 (issue #156)
-
v1.3, 2019-05-26
- 在发送信号时等待进程实际退出
- 这修复了 earlyoom 有时在只需终止一个进程时会终止多个进程的问题 (issue #121)
- 对 SIGTERM 和 SIGKILL 的限制值接受范围更宽松
(issue #97)
- 如果 SIGTERM 限制 < SIGKILL 限制,不要以致命错误退出
- 允许 SIGKILL 限制为零
- 重新格式化启动输出,以明确说明 swap 和 mem 都必须 <= limit
- 添加 notify_all_users.py 辅助脚本
- 添加 CODE_OF_CONDUCT.md (Contributor Covenant 1.4) (#102)
- 修复日志输出中可能被截断的 UTF8 应用名称 (#110)
- 在发送信号时等待进程实际退出
-
v1.2, 2018-10-28
-
v1.1, 2018-07-07
-
v1.0, 2018-01-28
- 添加
--prefer和--avoid选项(@TomJohnZ) - 添加对 GUI 通知的支持,添加选项
-n和-N
- 添加
-
v0.12: 添加
-M和-S选项(@nailgun);添加 man 页面,参数化 Makefile(@yangfl) -
v0.11: 修复 get_entry_fatal 中的未定义行为(缺少 return,commit)
-
v0.10: 允许覆盖 Makefile 中的 VERSION 变量以简化打包, 添加
-v命令行选项 -
v0.9: 若所有进程的 oom_score 均为 0,则使用 VmRss 来查找受害者
-
v0.8: 如果内核未提供 MemAvailable,则使用估算值
-
v0.7: 通过 oom_score 而非 VmRSS 选择受害者,添加选项
-i和-d -
v0.6: 添加命令行选项
-m、-s、-k -
v0.5: 添加交换分区支持
-
v0.4: 添加 SysV init 脚本(感谢 @joeytwiddle),使用
/proc/meminfo中的新MemAvailable(需要 Linux 3.14+,commit) -
v0.2: 添加 systemd 单元文件
-
v0.1: 初始版本