英語

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(),以便在資源壓力超過特定閾值時被喚醒。

觸發器描述了特定時間視窗內的最大累積停頓時間,例如在任何500ms視窗內總共100ms的停頓時間來生成喚醒事件。

要註冊觸發器,使用者必須開啟/proc/pressure/下代表要監視的資源的psi介面檔案,並寫入所需的閾值和時間視窗。 應使用開啟的檔案描述符來等待使用select()、poll()或epoll()的觸發器事件。 使用以下格式

<some|full> <stall amount in us> <time window in us>

例如,將“some 150000 1000000”寫入/proc/pressure/memory將為在1秒時間視窗內測量的部分記憶體停頓新增150ms的閾值。 將“full 50000 1000000”寫入/proc/pressure/io將為在1秒時間視窗內測量的完全io停頓新增50ms的閾值。

可以為多個psi指標設定觸發器,並且可以為同一psi指標指定多個觸發器。 但是,對於每個觸發器,都需要一個單獨的檔案描述符才能將其與其他觸發器分開輪詢,因此,即使開啟同一個psi介面檔案,對於每個觸發器都應進行單獨的open() syscall。 對已存在psi觸發器的檔案描述符的寫入操作將失敗,並顯示EBUSY。

監視器僅在系統進入受監視的psi指標的停頓狀態時啟用,並在退出停頓狀態後停用。 當系統處於停頓狀態時,以每個跟蹤視窗10次的比率監視psi訊號增長。

核心接受500ms到10s的視窗大小,因此最小監控更新間隔為50ms,最大為1s。 設定最小限制是為了防止過於頻繁的輪詢。 選擇最大限制是一個足夠高的數字,在此之後最有可能不需要監視器,而可以使用psi平均值代替。

非特權使用者也可以建立監視器,唯一的限制是視窗大小必須是2s的倍數,以防止過度使用資源。

啟用後,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: %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檔案系統的系統中,還會跟蹤分組到cgroup中的任務的壓力停頓資訊。 cgroupfs掛載點中的每個子目錄都包含cpu.pressure、memory.pressure和io.pressure檔案; 格式與/proc/pressure/檔案相同。

可以以與系統範圍的方式相同的方式指定和使用每個cgroup的psi監視器。