Cgroup 抽象層

Android 10 以上版本會使用控制群組 (cgroup) 抽象層,並搭配任務設定檔,開發人員可用來描述要套用至執行緒或程序的一組 (或多組) 限制。系統會依照工作設定檔的規定動作,選取一或多個適當的 cgroup,並透過這些 cgroup 套用限制,進而變更基礎 cgroup 功能集,而不會影響較高層級的軟體層。

關於 cgroups

Cgroup 提供一種機制,可將一組工作 (包含程序、執行緒和所有未來子項) 匯總及分割為具有特殊行為的階層群組。Android 使用 cgroups 控管及處理系統資源 (例如 CPU 和記憶體用量和分配),並支援 Linux 核心 cgroups v1cgroups v2

Android 9 以下版本

在 Android 9 以下版本中,init.rc 初始化指令碼包含可用的 cgroups 集合、掛載點和版本。雖然這些項目可以變更,但 Android 架構會根據指令碼,預期特定位置會存在特定的 cgroup 組合,並且具有特定版本和子群組階層。這會限制您選擇下一個要使用的 cgroup 版本,或變更 cgroup 階層以使用新功能的功能。

Android 10 以上版本

Android 10 以上版本會使用具有工作設定檔的 cgroups:

  • 群組設定。開發人員會在 cgroups.json 檔案中說明 cgroup 設定,以定義 cgroup 組合、其掛載位置和屬性。所有 cgroups 都會在初始化程序的早期初始化階段掛接。
  • 工作設定檔。這些抽象類別可將所需功能與其實作細節分開。Android 架構使用 SetTaskProfilesSetProcessProfiles API,將 task_profiles.json 檔案中所述的工作設定檔套用至處理程序或執行緒。(這些 API 僅適用於 Android 11 以上版本)。

為提供回溯相容性,舊版函式 set_cpuset_policyset_sched_policyget_sched_policy 提供相同的 API 和功能,但其實作已修改為使用工作設定檔。針對新用途,AOSP 建議使用新的任務設定檔 API,而非舊版 set_sched_policy 函式。

Cgroups 說明檔案

<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ 底下的 cgroups.json 檔案中,會說明 C 群組。每個控制器都會在子區段中說明,且必須至少包含下列項目:

  • 名稱,由「Controller」欄位定義。
  • 掛接路徑,由「Path」欄位定義。
  • ModeUID (使用者 ID) 和 GID (群組 ID),描述此路徑下檔案的擁有者和存取模式 (皆為選用項目)。
  • 選用屬性,設為 true 可讓系統忽略因核心不支援的 cgroup 控制器所導致的掛載錯誤。

cgroups.json 檔案範例

以下範例是 cgroup v1 (Cgroups) 和 cgroup v2 (Cgroups2) 控制器的說明,其中包含各自的路徑。

{
  "Cgroups": [
    {
      "Controller": "cpu",
      "Path": "/dev/cpuctl",
      "Mode": "0755",
      "UID": "system",
      "GID": "system"
    },
    {
      "Controller": "memory",
      "Path": "/dev/memcg",
      "Mode": "0700",
      "Optional": true
    }
  ],
 "Cgroups2": {
   "Path": "/sys/fs/cgroup",
   "Mode": "0755",
   "UID": "system",
   "GID": "system",
   "Controllers": [
     {
       "Controller": "freezer",
       "Path": ".",
       "Mode": "0755",
       "UID": "system",
       "GID": "system"
     }
   ]
 }
}

這個範例檔案包含兩個區段:Cgroups (說明 cgroup v1 控制器) 和 Cgroups2 (描述 cgroup v2 控制器)。cgroups v2 階層中的所有控制器都會掛接在同一個位置。因此,Cgroups2 區段有其專屬的 PathModeUIDGID 屬性,用於描述階層根目錄的位置和屬性。Cgroups2 底下「Controllers」的「Path」屬性會相對於該根路徑。在 Android 12 以上版本中,您可以定義 cgroup 控制器,並將路徑和模式指定為 "Optional",方法是將其設為 true

在初始化階段的早期,系統會在初始化程序中剖析 cgroups.json 檔案,並在指定位置掛載 cgroups。如要日後取得 cgroup 掛載位置,請使用 CgroupGetControllerPath API 函式。

工作設定檔檔案

task_profiles.json 檔案位於 <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ 下方。可用於描述要套用至程序或執行緒的特定動作集。一組動作會與設定檔名稱相關聯,這會用於 SetTaskProfilesSetProcessProfiles 呼叫,以便叫用設定檔動作。

範例 task_profiles.json 檔案

{
  "Attributes": [
    {
      "Name": "MemSoftLimit",
      "Controller": "memory",
      "File": "memory.soft_limit_in_bytes"
    },
    {
      "Name": "MemSwappiness",
      "Controller": "memory",
      "File": "memory.swappiness"
    }
  ],
  "Profiles": [
    {
      "Name": "MaxPerformance",
      "Actions" : [
        {
          "Name" : "JoinCgroup",
          "Params" :
          {
            "Controller": "schedtune",
            "Path": "top-app"
          }
        }
      ]
    },
    {
      "Name": "TimerSlackHigh",
      "Actions" : [
        {
          "Name" : "SetTimerSlack",
          "Params" :
          {
            "Slack": "40000000"
          }
        }
      ]
    },
    {
      "Name": "LowMemoryUsage",
      "Actions" : [
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSoftLimit",
            "Value" : "16MB"
          }
        },
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSwappiness",
            "Value" : "150"

          }
        }
      ]
    }
  ]
  "AggregateProfiles": [
     {
       "Name": "SCHED_SP_DEFAULT",
       "Profiles": [ "TimerSlackHigh", "MaxPerformance" ]
     },
     {
       "Name": "SCHED_SP_BACKGROUND",
       "Profiles": [ "LowMemoryUsage" ]
     }
}

為特定 cgroup 檔案指派名稱,做為「Attributes」清單中的項目。每個項目都包含下列項目:

  • 「名稱」欄位會指定屬性名稱。
  • 「Controller」欄位會根據名稱參照 cgroups.json 檔案中的 cgroup 控制器。
  • 「File」欄位會命名這個控制器底下的特定檔案。

「屬性」是工作設定檔定義中的參照。除了工作設定檔之外,只有在架構需要直接存取這些檔案,且無法使用工作設定檔抽象化存取權時,才使用工作設定檔。在所有其他情況下,請使用工作設定檔,因為這類設定檔可在必要行為與其實作細節之間提供更佳的解耦。

「Profiles」部分包含工作資料夾定義,其中包含以下內容:

  • 「Name」欄位定義設定檔名稱。
  • 「Actions」(動作) 區段會列出套用設定檔時要執行的一組動作。每個動作都包含以下項目:

    • 「Name」欄位會指定動作。
    • 「Params」部分會指定一組操作參數。

支援的動作列於下表:

動作 參數 說明
SetTimerSlack Slack 計時器鬆弛時間 (以奈秒為單位)
SetAttribute Name 參照「Attributes」區段中屬性的名稱
Value 要寫入以指定屬性表示檔案的值
WriteFileFilePath檔案路徑
Value要寫入檔案的值
JoinCgroup Controller cgroups.json 中的 cgroup 控制器名稱
Path cgroup 控制器階層中的子群組路徑

Android 12 以上版本提供「AggregateProfiles」區段,其中包含匯總設定檔,每個匯總設定檔都是一或多個設定檔的別名。匯總設定檔定義包含下列項目:

  • 「名稱」欄位會指定匯總設定檔的名稱。
  • 「Profiles」欄位會列出匯總設定檔中包含的設定檔名稱。

套用匯總設定檔時,系統也會自動套用所有包含的設定檔。匯總設定檔可包含個別設定檔或其他匯總設定檔,但不得有遞迴 (包含自身的設定檔)。

task_profiles 初始化語言指令

Android Init Language 中的 task_profiles 指令適用於 Android 12 以上版本,可為特定程序啟用工作設定檔。這會取代用來在 cgroups 之間遷移程序的 writepid 指令 (Android 12 中已淘汰)。task_profiles 指令可彈性地變更基礎實作項目,不會對上層造成影響。在下列範例中,這兩個指令實際上執行相同的作業:

  • writepid /dev/cpuctl/top-app/tasks

    已於 Android 12 淘汰,這可用於將目前工作的 PID 寫入 /dev/cpuctl/top-app/tasks 檔案。

  • task_profiles MaxPerformance

    將目前的程序加入「cpu」控制器 (cpuctl) 下的頂層應用程式群組,這會導致將程序的 PID 寫入 dev/cpuctl/top-app/tasks

在 Android 12 以上版本中,請一律使用 task_profiles 指令來遷移 cgroup 階層中的任務。此方法可接受一或多個參數,代表 task_profiles.json 檔案中指定的設定檔名稱。

個別 API 層級的工作設定檔

在 Android 12 以上版本中,您可以修改或覆寫預設 cgroups.jsontask_profiles.json 檔案中的定義,方法是根據 Android API 級別進行變更,或從供應商分區進行變更。

如要覆寫依 API 級別而定的定義,裝置上必須有下列檔案:

  • /system/etc/task_profiles/cgroups_<API level>.json

    請將此值用於 API 級別專屬的 cgroup。

  • /system/etc/task_profiles/task_profiles_<API level>.json

    請將此值用於特定 API 級別的設定檔。

如要覆寫供應商分割區的定義,裝置上必須有下列檔案:

  • /vendor/etc/cgroups.json
  • /vendor/etc/task_profiles.json

如果這些檔案中的屬性或設定檔定義與預設檔案中的名稱相同,則檔案 (API 級別或供應商級別) 定義會覆寫先前的定義。請注意,供應商層級定義會覆寫 API 層級定義。如果新定義有新名稱,則一組屬性或設定檔會採用新定義。

Android 系統會按照下列順序載入 cgrouptask_profile 檔案:

  1. 預設的 cgroups.jsontask_profiles.json 檔案。
  2. API 級別專屬檔案 (如果有)。
  3. 廠商分區檔案 (如有)。

現有 API 的變更

Android 10 以上版本可在不變更 API 的情況下保留 set_cpuset_policyset_sched_policyget_sched_policy 函式。不過,Android 10 會將這些函式移至 libprocessgroup,後者現在包含所有 cgroup 相關功能。

雖然 cutils/sched_policy.h 標頭仍存在,但為了避免中斷現有程式碼,請確保新程式碼包含新的 processgroup/sched_policy.h 標頭。

使用上述任一函式的模組應在 makefile 中加入 libprocessgroup 程式庫的依附元件。如果模組未使用任何其他 libcutils 功能,請從 makefile 中移除 libcutils 程式庫依附元件。

工作設定檔 API

processgroup/processgroup.h 中的私人 API 已在下表中定義:

類型 API 和定義
bool SetTaskProfiles(int tid, const std::vector& profiles)
profiles 中指定的工作設定檔,套用至使用其 tid 參數指定的執行緒 ID (tid) 的執行緒。
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
使用 uidpid 參數,將 profiles 中指定的工作設定檔套用至使用者和程序 ID 指定的程序
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
會傳回 cgroup_name 指定的 cgroup 控制器是否存在;如果 true,則將 path 變數設為該 cgroup 的根目錄
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
會傳回 attr_name 指定的設定檔屬性是否存在;如果存在,true 會將 path 變數設為與該設定檔屬性相關聯的檔案路徑。
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
會傳回 attr_name 指定的設定檔屬性是否存在;如果 true,則會將 path 變數設為與該設定檔屬性相關聯的檔案路徑,以及使用 tid 參數指定的執行緒 ID 所指定的執行緒。
bool UsePerAppMemcg()
傳回系統是否設為使用個別應用程式記憶體 cgroups。