Cgroup 抽象層

Android 10 及更高版本使用帶有任務配置文件的控制組 (cgroup) 抽象層,開發人員可以使用它來描述一組(或多組)限制以應用於線程或進程。然後系統按照任務配置文件的規定動作來選擇一個或多個適當的 cgroup,通過這些 cgroup 應用限制,並且可以在不影響更高軟件層的情況下對底層 cgroup 功能集進行更改。

關於 cgroups

Cgroups 提供了一種機制,用於將任務集(包括進程、線程及其所有未來的子進程)聚合和劃分為具有特殊行為的分層組。 Android 使用 cgroups 來控制和計算系統資源,例如 CPU 和內存的使用和分配,並支持 Linux 內核cgroups v1cgroups v2

安卓 9 及更低版本

在 Android 9 及更低版本中, init.rc初始化腳本包含一組可用的 cgroup、它們的掛載點和版本。雖然這些可以更改,但 Android 框架希望基於腳本的特定位置存在一組特定的 cgroup,具有特定的版本和子組層次結構。這限制了選擇要使用的下一個 cgroup 版本或更改 cgroup 層次結構以使用新功能的能力。

Android 10 及更高版本

Android 10 及更高版本使用帶有任務配置文件的 cgroup:

  • Cgroup 設置 - 開發人員在其cgroups.json文件中描述 cgroup 設置以定義 cgroup 集及其安裝位置和屬性。所有 cgroup 都在初始化過程的 early-init 階段掛載。
  • 任務配置文件——這些提供了一種抽象,將所需的功能與其實現的細節分離。 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文件中進行了描述。每個控制器都在一個小節中進行了描述,並且必須至少具有以下內容:

  • 名稱,由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控制器Path屬性是相對於該根路徑的。在 Android 12 及更高版本中,您可以通過將其設置為true來定義使用路徑和模式指定為"Optional"的 cgroup 控制器。

cgroups.json文件作為初始化過程的一部分在 early-init 階段被解析,並且 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 文件作為屬性列表中的條目。每個條目包含以下內容:

  • 名稱字段 - 指定屬性的名稱。
  • 控制器字段 - 按名稱引用cgroups.json文件中的 cgroup 控制器。
  • 文件字段 - 命名此控制器下的特定文件。

屬性是任務配置文件定義中的引用。在任務配置文件之外,當框架需要直接訪問這些文件並且無法使用任務配置文件抽象訪問時才使用它們。在所有其他情況下,使用任務配置文件;它們在所需行為與其實現細節之間提供了更好的解耦。

Profiles部分包含具有以下內容的任務配置文件定義:

  • 名稱字段 - 定義配置文件名稱。
  • 操作部分 - 列出應用配置文件時執行的一組操作。每個動作都有以下內容:

    • 指定操作的名稱字段
    • 指定動作的一組參數的參數部分

下表列出了支持的操作。

行動範圍描述
SetTimerSlack Slack以 ns 為單位的計時器鬆弛
SetAttribute Name引用“屬性”部分中的屬性的名稱
Value要寫入由命名屬性表示的文件的值
WriteFile FilePath文件路徑
Value要寫入文件的值
JoinCgroup Controller cgroups.json中的 cgroup 控制器的名稱
Path cgroup 控制器層次結構中的子組路徑

Android 12 及更高版本具有包含聚合配置文件的AggregateProfiles部分,每個配置文件都是一組一個或多個配置文件的別名。聚合配置文件定義包括以下內容:

  • 名稱字段 - 指定聚合配置文件的名稱。
  • 配置文件字段 - 列出聚合配置文件中包含的配置文件的名稱。

應用聚合配置文件時,也會自動應用所有包含的配置文件。聚合配置文件可以包含單個配置文件或其他聚合配置文件,只要沒有遞歸(包含自身的配置文件)。

task_profiles 初始化語言命令

Android 初始化語言中的task_profiles命令可用於 Android 12 及更高版本,以促進特定進程的任務配置文件激活。它取代了用於在 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 )下的 top-app 組,從而將進程的 PID 寫入dev/cpuctl/top-app/tasks

在 Android 12 及更高版本中,始終使用task_profiles命令遷移 cgroup 層次結構中的任務。它接受一個或多個參數,代表task_profiles.json文件中指定的配置文件的名稱。

每個 API 級別的任務配置文件

在 Android 12 及更高版本中,您可以修改或覆蓋默認cgroups.jsontask_profiles.json文件中的定義,既可以基於 Android API 級別的更改,也可以從供應商分區進行更改。

要覆蓋基於 API 級別的定義,設備上必須存在以下文件:

  • pro/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標頭。

使用這些函數的模塊應該將libprocessgroup庫的依賴添加到它們的 makefile 中。如果模塊不使用任何其他libcutils功能,請從 makefile 中刪除libcutils庫依賴項。

任務配置文件 API

processgroup/processgroup.h中的私有 API 定義如下表:

類型API 和定義
bool SetTaskProfiles(int tid, const std::vector & profiles) SetTaskProfiles(int tid, const std::vector & profiles)

使用其tid參數將配置文件中指定的任務profiles應用於由線程 ID (tid) 指定的線程。

bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) 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。