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 以上版本會使用具有工作資料夾的 cgroup:

  • 群組設定。開發人員會在 cgroups.json 檔案中說明 cgroup 設定,以定義 cgroup 組合、其掛載位置和屬性。所有 cgroup 會在初始化程序的早期初始化階段掛載。
  • 工作設定檔。這些抽象類別可將所需功能與其實作細節分開。Android 架構會將 task_profiles.json 檔案中所述的工作設定檔,套用至使用 SetTaskProfilesSetProcessProfiles API 的程序或執行緒。(這些 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」欄位定義設定檔名稱。
  • 「動作」部分會列出套用設定檔時執行的一組動作。每個動作都包含以下項目:

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

支援的動作列於下表:

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

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

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

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

task_profiles init language 指令

Android 初始化語言中,Android 12 以上版本提供 task_profiles 指令,可協助啟用特定程序的工作設定檔。這個指令取代了 writepid 指令 (已在 Android 12 中淘汰),後者用於在 cgroup 之間遷移程序。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 以上版本會保留 set_cpuset_policyset_sched_policyget_sched_policy 函式,且不會變更 API。不過,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()
會傳回系統是否已設定為使用個別應用程式記憶體 cgroup。