- A+
Pressure Stall Information
压力失速信息
Date: April, 2018
Author: Johannes Weiner hannes@cmpxchg.org
当CPU、MEM或者IO设备被争夺时,工作负载就会经受延迟增加,吞吐量损失和运行时被OOM杀死的风险。
如果没有资源竞争的精确测量,用户被迫或者安全操作然后充分利用硬件资源,或者赌上一把然后频繁遭遇到程序中断的后果。
PSI功能识别和量化这些中断,他们由大量资源处理引起的、复杂工作负载中的时间影响到的,或者甚至由整个系统造成的。
有一种由资源稀缺引起的生产效率损失的精确测量方法,可以帮助大工作负载下的用户来根据工作量需求做硬件化或者提供硬件化。
因为PSI实时收集这些信息,系统能使用这些技术进行动态管理,例如负载调度、迁移任务到其他的系统或者数据中心、或者策略性地暂停或者杀死低优先级或可重启的批处理任务。
这个特性允许最大化硬件利用率而不用牺牲工作负载健康或者承担OOM杀死的中断风险。
压力接口
每种资源的压力信息通过/proc/pressure/下的文件(cpu、memory、io)导出来。
格式如下:
some avg10=0.00 avg60=0.00 avg300=0.00 total=0 full avg10=0.00 avg60=0.00 avg300=0.00 total=0
"some"行表示至少有一些任务在给定资源上失速的时间份额。
"full"行表示所有非空闲任务在给定资源上同时失速的时间份额。在这个状态下,实际的CPU周期将会浪费掉,花费了额外时间的工作负载被认为受到了冲击。这会对性能有严重的影响,一些任务正在失速但是CPU仍然在做大量的工作,识别出这种状态是很有用的。花费在这些失速状态下的时间可以分别跟踪并导入到“full”平均值中。
CPU full没有在系统中定义,但是从5.13版本已经开始报告了,为了后向兼容,它被设置为0。
百分比跟踪的是最近10/60/300秒窗口趋势,可以跟踪短期和中长期的趋势。总的绝对失速时间(us)用来探测延迟增加,它不需要平均时间或者时间窗口的平均趋势。
压力阈的监测
用户可以注册触发器然,当资源压力超过特定阈值时使用poll()来唤醒。
触发器描述了整个特定时间窗口内的最大累计失速时间,例如在任何500ms窗口内,总的失速时间达100ms就产生一个唤醒事件。
要注册触发器,用户必须打开/proc/pressure下的PSI接口文件,写入期望的阈值和时间窗口。打开文件的描述符应该使用select()、poll() 或者 epoll()来等待触发器。使用下面的格式:
<some|full> <stall amount in us> <time window in us>
举个例子,写入“some 150000 1000000”到/proc/pressure/memory,将会加入150ms的阈值来做1秒时间窗口内的memory失速测量。写入“full 50000 1000000”到/proc/pressure/io将加入50ms阈值来做1秒时间窗口内的full io失速测量.
触发器可以设置多个PSI测量单位,也可以为相同的PSI测量单位定义多个触发器。然而每个触发器需要独立的文件描述符来各自地轮询,因而当相同的psi接口文件正在打开,每个触发器的open()系统调用也可以打开它。对已经存在psi触发器的文件描述符的写操作会触发EBUSY失败。
只有当系统进入失速状态监视器才会激活,退出失速状态就会去活。当系统在失速状态,psi信号增长是以每个跟踪窗口的十倍比率监测的。
内核接受窗口大小的范围500ms~10s,因此最小监测更新间隔是50ms,最大是1s。设置最小限制是为了防止跟随后的轮询产生时间叠压。选择最大限制是因为更高的监视窗口几乎是不需要的,可以用psi平均值来代替。
当激活之后,psi监视器在跟踪窗口期间会保持激活状态,避免当系统进出失速状态时反复的激活/去活。
给用户空间的通知频率限制为每个跟踪窗口一次。。
触发器文件描述符被关闭时,触发器会解除注册。
用户空间监视器使用示例
#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <string.h> #include <unistd.h> /* * Monitor memory partial stall with 1s tracking window size * and 150ms threshold. */ int main() { const char trig[] = "some 150000 1000000"; struct pollfd fds; int n; fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK); if (fds.fd < 0) { printf("/proc/pressure/memory open error: %sn", strerror(errno)); return 1; } fds.events = POLLPRI; if (write(fds.fd, trig, strlen(trig) + 1) < 0) { printf("/proc/pressure/memory write error: %sn", strerror(errno)); return 1; } printf("waiting for events...n"); while (1) { n = poll(&fds, 1, -1); if (n < 0) { printf("poll error: %sn", strerror(errno)); return 1; } if (fds.revents & POLLERR) { printf("got POLLERR, event source is gonen"); return 0; } if (fds.revents & POLLPRI) { printf("event triggered!n"); } else { printf("unknown event received: 0x%xn", fds.revents); return 1; } } return 0; }
Cgroup2接口
系统内核配置了CONFIG_CGROUP=y,cgroup2文件系统已被挂载,压力失速信息也用来跟踪cgroup中的分组任务。cgroupfs挂载点下的每个子目录
包含cpu.pressure、memory.pressure和io.pressure文件,他们的格式跟/proc/pressure一样。
每个cgroup的psi监视器可以跟系统级的定义和使用方式完全一样。
英文原文:
https://www.kernel.org/doc/html/latest/accounting/psi.html