Cgroup 抽象層

Android 10 以上版本會使用控制群組 (cgroup) 抽象層和工作設定檔,開發人員可用於描述要套用至執行緒或程序的限制集。然後,系統會按照工作設定檔的規定動作選取一或多個適當的 cgroup,藉此套用限制,並變更基礎 cgroup 功能集,不會影響較高的軟體層。

關於 cgroup

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

Android 9 以下版本

在 Android 9 以下版本中,init.rc 初始化指令碼包含一組可用的 Cgroup、掛接點和版本。雖然這些項目可以變更,但 Android 架構預期特定的一組 Cgroup 會存在於特定位置,並具有特定版本和子群組階層,這是根據指令碼而定。這項限制導致使用者無法選擇要使用的下一個 cgroup 版本,也無法變更 cgroup 階層來使用新功能。

Android 10 以上版本

Android 10 以上版本會使用 cgroup 和工作設定檔:

  • 設定 Cgroup。開發人員會在 cgroups.json 檔案中說明 cgroup 設定,定義 cgroup 集,以及其掛接位置和屬性。所有 cgroup 都會在初始化程序的早期初始化階段掛接。
  • 工作設定檔。這些介面提供抽象化功能,可將必要功能與實作詳細資料分離。Android 架構會使用 SetTaskProfilesSetProcessProfiles API,將 task_profiles.json 檔案中說明的任務設定檔套用至程序或執行緒。(這些 API 僅適用於 Android 11 以上版本)。

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

Cgroups 說明檔案

Cgroup 說明位於 <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ 下的 cgroups.json 檔案中。每個控制器都會在子章節中說明,且至少須具備下列項目:

  • 名稱,由「控制器」欄位定義。
  • 掛接路徑,由「路徑」欄位定義。
  • 模式UID (使用者 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」部分有自己的「Path」、「Mode」、「UID」和「GID」屬性,可說明階層根目錄的位置和屬性。「Cgroups2」下「控制器」的「路徑」屬性是相對於該根路徑。在 Android 12 以上版本中,您可以定義以路徑和模式指定為 "Optional" 的 cgroup 控制器,方法是將其設為 true

在早期初始化階段,系統會在初始化程序中剖析 cgroups.json 檔案,並將 cgroup 掛接至指定位置。如要稍後取得 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 檔案做為項目。 每個項目都包含下列內容:

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

屬性是工作設定檔定義中的參照。除了工作設定檔之外,只有在架構需要直接存取這些檔案,且無法使用工作設定檔抽象化存取權時,才使用這些檔案。在所有其他情況下,請使用工作設定檔,因為這樣可更妥善地將必要行為與實作詳細資料分離。

「設定檔」部分包含工作設定檔定義,其中包含:

  • 「名稱」欄位會定義設定檔名稱。
  • 「動作」部分會列出套用設定檔時執行的一組動作。每項動作都有下列屬性:

    • 「名稱」欄位會指定動作。
    • 「Params」部分會指定動作的一組參數。

下表列出支援的動作:

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

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

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

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

task_profiles init 語言指令

Android 12 以上版本提供 Android Init Language 中的 task_profiles 指令,可為特定程序啟用工作設定檔。這個指令會取代用於在 cgroup 之間遷移程序的 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 以上版本中,您可以根據 Android API 級別,或從供應商分割區進行變更,修正或覆寫預設 cgroups.jsontask_profiles.json 檔案中的定義。

如要根據 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)
使用 tid 參數,將 profiles 中指定的工作設定檔套用至以執行緒 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 指定) 是否存在;如果存在,則將 path 變數設為與該設定檔屬性相關聯的檔案路徑。true
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
傳回 attr_name 指定的設定檔屬性是否存在;如果存在,則使用 tid 參數,將 path 變數設為與該設定檔屬性相關聯的檔案路徑,以及其執行緒 ID 指定的執行緒。true
bool UsePerAppMemcg()
傳回系統是否已設定為使用每個應用程式的記憶體 Cgroup。