PSI - Pressure Stall Information (压力停滞信息) 【ChatGPT】

发布时间 2023-12-08 19:43:53作者: 摩斯电码

原文:https://www.kernel.org/doc/html/v6.6/accounting/psi.html#psi

日期

2018年4月

作者

Johannes Weiner hannes@cmpxchg.org

当CPU、内存或IO设备争用时,工作负载会出现延迟峰值、吞吐量损失,并面临OOM杀死的风险。

缺乏对这种争用的准确衡量,用户不得不在谨慎使用硬件资源或频繁遭受过度承诺带来的干扰之间做出选择。

PSI功能能够识别和量化由资源紧缺引起的干扰,以及它对复杂工作负载甚至整个系统的时间影响。

准确衡量由资源稀缺引起的生产力损失有助于用户根据工作负载需求调整硬件大小或根据工作负载需求配置硬件。

由于PSI实时聚合这些信息,系统可以通过负载分担、将作业迁移到其他系统或数据中心,或者有策略地暂停或终止低优先级或可重新启动的批处理作业等技术进行动态管理。

这样可以最大限度地利用硬件资源,而不会牺牲工作负载的健康或冒险面临诸如OOM杀死之类的重大干扰。

压力接口

每个资源的压力信息通过/proc/pressure/中的相应文件导出--cpu、内存和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以来已经报告,因此为了向后兼容性,它被设置为零。

这些比率(以%表示)是在十、六十和三百秒窗口内跟踪的最近趋势,这可以洞察短期事件以及中长期趋势。还跟踪并导出了总的绝对阻塞时间(以微秒为单位),以便检测可能不会在时间平均值中产生影响的延迟峰值,或者以便在自定义时间范围内平均趋势。

监控压力阈值

用户可以注册触发器,并使用poll()在资源压力超过一定阈值时被唤醒。

触发器描述了在特定时间窗口内的最大累积阻塞时间,例如在任何500毫秒窗口内的总阻塞时间达到100毫秒以生成唤醒事件。

要注册一个触发器,用户必须打开/proc/pressure/下代表要监视的资源的psi接口文件,并写入所需的阈值和时间窗口。应使用打开的文件描述符使用select()、poll()或epoll()等方法等待触发事件。格式如下:

<some|full> <阻塞时间(以微秒为单位)> <时间窗口(以微秒为单位)>

例如,将"some 150000 1000000"写入/proc/pressure/memory将在1秒时间窗口内添加150毫秒的部分内存阻塞阈值。将"full 50000 1000000"写入/proc/pressure/io将在1秒时间窗口内添加50毫秒的完全io阻塞阈值。

可以在多个psi指标上设置触发器,并且可以为同一psi指标指定多个触发器。但是,对于每个触发器,都需要一个单独的文件描述符,以便能够单独对其进行轮询,因此即使打开相同的psi接口文件,也应该进行单独的open()系统调用。当打开已存在psi触发器的文件描述符时,对文件描述符的写操作将失败,并返回EBUSY。

监视器仅在系统进入监视的psi指标的停滞状态时激活,并在退出停滞状态时停用。在系统处于停滞状态时,psi信号增长以每个跟踪窗口的10倍速率进行监视。

内核接受从500毫秒到10秒的窗口大小,因此最小监视更新间隔为50毫秒,最大为1秒。最小限制是为了防止过于频繁的轮询。最大限制是选择一个足够大的数字,之后监视器可能不再需要,并且可以使用psi平均值代替。

非特权用户也可以创建监视器,唯一的限制是窗口大小必须是2秒的倍数,以防止过度的资源使用。

激活后,psi监视器将保持激活状态,至少持续一个跟踪窗口的时间,以避免在系统在停滞状态中反复激活/停用。

向用户空间的通知被限制为每个跟踪窗口一次。

当用于定义触发器的文件描述符关闭时,触发器将被注销。

用户空间监视器使用示例

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <unistd.h>

/*
 * 使用1秒的跟踪窗口大小和150毫秒的阈值来监视部分内存停滞。
 */
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: %s\n",
                      strerror(errno));
              return 1;
      }
      fds.events = POLLPRI;

      if (write(fds.fd, trig, strlen(trig) + 1) < 0) {
              printf("/proc/pressure/memory write error: %s\n",
                      strerror(errno));
              return 1;
      }

      printf("waiting for events...\n");
      while (1) {
              n = poll(&fds, 1, -1);
              if (n < 0) {
                      printf("poll error: %s\n", strerror(errno));
                      return 1;
              }
              if (fds.revents & POLLERR) {
                      printf("got POLLERR, event source is gone\n");
                      return 0;
              }
              if (fds.revents & POLLPRI) {
                      printf("event triggered!\n");
              } else {
                      printf("unknown event received: 0x%x\n", fds.revents);
                      return 1;
              }
      }

      return 0;
}

Cgroup2接口

在一个内核中启用了CONFIG_CGROUPS=y并且挂载了cgroup2文件系统的系统中,对分组到cgroups中的任务也会跟踪压力停滞信息。cgroupfs挂载点中的每个子目录都包含cpu.pressure、memory.pressure和io.pressure文件;格式与/proc/pressure/文件相同。

可以指定每个cgroup的psi监视器,并且与系统范围的监视器使用方式相同。