英文

每個任務的統計資訊介面

Taskstats 是一個基於 netlink 的介面,用於將每個任務和每個程序的統計資訊從核心傳送到使用者空間。

Taskstats 的設計具有以下優點:

  • 在任務的生命週期內及其退出時,有效地提供統計資訊

  • 多個統計子系統的統一介面

  • 可擴充套件性,供將來的統計補丁使用

術語

“pid”、“tid”和“task”可以互換使用,指的是由 struct task_struct 定義的標準 Linux 任務。每個 pid 的統計資訊與每個任務的統計資訊相同。

“tgid”、“process”和“thread group”可以互換使用,指的是共享 mm_struct 的任務,即傳統的 Unix 程序。儘管使用了 tgid,但執行緒組的領導者任務沒有特殊待遇 - 只要程序中包含屬於它的任何任務,該程序就被認為是存活的。

用法

為了在任務的生命週期內獲取統計資訊,使用者空間開啟一個單播 netlink 套接字(NETLINK_GENERIC 族),併發送指定 pid 或 tgid 的命令。響應包含任務的統計資訊(如果指定了 pid),或程序的所有任務的統計資訊之和(如果指定了 tgid)。

為了獲取正在退出的任務的統計資訊,使用者空間偵聽器傳送一個註冊命令並指定一個 cpumask。每當一個任務在 cpumask 中的某個 CPU 上退出時,其每個 pid 的統計資訊都會發送到註冊的偵聽器。使用 cpumask 允許限制一個偵聽器接收的資料,並有助於 netlink 介面上的流量控制,這將在下面詳細說明。

如果退出的任務是退出其執行緒組的最後一個執行緒,則還會向用戶空間傳送一條包含每個 tgid 統計資訊的附加記錄。後者包含執行緒組中所有執行緒(包括過去和現在的執行緒)的每個 pid 統計資訊之和。

getdelays.c 是一個簡單的實用程式,演示了 taskstats 介面用於報告延遲統計資訊的用法。使用者可以註冊 cpumask,傳送命令和處理響應,偵聽每個 tid/tgid 的退出資料,將接收到的資料寫入檔案,並透過增加接收緩衝區大小來執行基本的流量控制。

介面

使用者-核心介面封裝在 include/linux/taskstats.h 中

為了避免此文件隨著介面的發展而過時,此處僅給出當前版本的概要。taskstats.h 始終會覆蓋此處的描述。

struct taskstats 是每個 pid 和每個 tgid 資料的通用統計結構。它已版本化,並且可以由新增到核心的每個統計子系統進行擴充套件。欄位及其語義在 taskstats.h 檔案中定義。

使用者空間和核心空間之間交換的資料是一個屬於 NETLINK_GENERIC 族的 netlink 訊息,並使用 netlink 屬性介面。訊息的格式如下:

+----------+- - -+-------------+-------------------+
| nlmsghdr | Pad |  genlmsghdr | taskstats payload |
+----------+- - -+-------------+-------------------+

taskstats 負載是以下三種類型之一:

1. 命令:從使用者傳送到核心。用於獲取 pid/tgid 資料的命令由一個屬性組成,型別為 TASKSTATS_CMD_ATTR_PID/TGID,包含屬性負載中的 u32 pid 或 tgid。pid/tgid 表示使用者空間想要統計資訊的任務/程序。

用於註冊/登出對來自一組 CPU 的退出資料的興趣的命令由一個屬性組成,型別為 TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK,幷包含屬性負載中的 cpumask。cpumask 被指定為逗號分隔的 CPU 範圍的 ascii 字串,例如,要偵聽來自 CPU 1,2,3,5,7,8 的退出資料,cpumask 將為“1-3,5,7-8”。如果使用者空間在關閉偵聽套接字之前忘記登出對 CPU 的興趣,則核心會隨著時間的推移清理其興趣集。但是,為了提高效率,建議顯式登出。

2. 命令的響應:從核心傳送到使用者空間以響應使用者空間命令。負載是一系列三個屬性,型別為:

a) TASKSTATS_TYPE_AGGR_PID/TGID:屬性不包含負載,但指示 pid/tgid 後面將跟隨一些統計資訊。

b) TASKSTATS_TYPE_PID/TGID:屬性的負載是要返回其統計資訊的 pid/tgid。

c) TASKSTATS_TYPE_STATS:屬性以 struct taskstats 作為負載。相同的結構用於每個 pid 和每個 tgid 的統計資訊。

  1. 每當任務退出時,核心傳送的新訊息。負載由以下型別的屬性組成:

  1. TASKSTATS_TYPE_AGGR_PID:指示接下來的兩個屬性將是 pid + 統計資訊

  2. TASKSTATS_TYPE_PID:包含退出任務的 pid

  3. TASKSTATS_TYPE_STATS:包含退出任務的每個 pid 的統計資訊

  4. TASKSTATS_TYPE_AGGR_TGID:指示接下來的兩個屬性將是 tgid + 統計資訊

  5. TASKSTATS_TYPE_TGID:包含任務所屬程序的 tgid

  6. TASKSTATS_TYPE_STATS:包含退出任務程序的每個 tgid 的統計資訊

每個 tgid 的統計資訊

Taskstats 除了提供每個任務的統計資訊外,還提供每個程序的統計資訊,因為資源管理通常以程序粒度完成,並且僅在使用者空間中聚合任務統計資訊效率低下且可能不準確(由於缺乏原子性)。

但是,在核心中維護每個程序(除了每個任務)的統計資訊具有空間和時間開銷。為了解決這個問題,taskstats 程式碼將每個退出任務的統計資訊累積到一個程序範圍內的資料結構中。當程序的最後一個任務退出時,也會將累積的程序級別資料(以及每個任務的資料)傳送到使用者空間。

當用戶查詢以獲取每個 tgid 的資料時,組中所有其他活動執行緒的總和將被加起來,並新增到同一執行緒組先前退出的執行緒的累積總和中。

擴充套件 taskstats

有兩種方法可以擴充套件 taskstats 介面,以匯出更多的每個任務/程序統計資訊,因為收集它們的補丁會在將來新增到核心中:

  1. 在現有 struct taskstats 的末尾新增更多欄位。向後相容性由結構中的版本號確保。使用者空間將僅使用結構中與其正在使用的版本相對應的欄位。

  2. 定義單獨的統計結構,並使用 netlink 屬性介面返回它們。由於使用者空間獨立處理每個 netlink 屬性,因此它始終可以忽略其型別不理解的屬性(因為它使用的是介面的舊版本)。

在 1 和 2 之間進行選擇是權衡靈活性和開銷的問題。如果只需要新增少量欄位,那麼 1 是首選路徑,因為核心和使用者空間不需要承擔處理新 netlink 屬性的開銷。但是,如果新欄位過度擴充套件了現有結構,導致不同的使用者空間統計實用程式不必要地接收到大量對其欄位不感興趣的結構,那麼擴充套件屬性結構將是值得的。

taskstats 的流量控制

當任務退出速率變得很大時,偵聽器可能無法跟上核心傳送每個 tid/tgid 退出資料的速率,從而導致資料丟失。當 taskstats 結構被擴充套件並且 CPU 數量變得很大時,這種可能性會變得更加複雜。

為了避免丟失統計資訊,使用者空間應執行以下一項或多項操作:

  • 增加偵聽器開啟的 netlink 套接字的接收緩衝區大小,以接收退出資料。

  • 建立更多偵聽器並減少每個偵聽器正在偵聽的 CPU 數量。在極端情況下,每個 CPU 可以有一個偵聽器。使用者還可以考慮將偵聽器的 CPU 親和性設定為它正在偵聽的 CPU 子集,特別是如果它們只偵聽一個 CPU。

儘管採取了這些措施,如果使用者空間收到指示接收緩衝區溢位的 ENOBUFS 錯誤訊息,則應採取措施處理資料丟失。