Android 10 及更高版本使用具有任務設定檔的控制組 (cgroup) 抽象層,開發人員可以使用它來描述應用於執行緒或進程的一組(或多組)限制。然後,系統會依照任務設定檔的規定操作來選擇一個或多個適當的 cgroup,透過這些 cgroup 進行限制,並且可以在不影響更高軟體層的情況下對底層 cgroup 功能集進行變更。
關於 cgroup
Cgroup 提供了一種機制,用於將任務集(由進程、執行緒及其所有未來子級組成)聚合和分區為具有專門行為的分層群組。 Android 使用 cgroup 來控制和統計系統資源,例如 CPU 和記憶體的使用和分配,並支援 Linux 核心cgroups v1和cgroups v2 。
Android 9 及更低版本
在 Android 9 及更低版本中, init.rc
初始化腳本包含一組可用的 cgroup、它們的安裝點和版本。雖然這些可以更改,但 Android 框架期望根據腳本在特定位置存在一組特定的 cgroup,具有特定版本和子組層次結構。這限制了選擇要使用的下一個 cgroup 版本或更改 cgroup 層次結構以使用新功能的能力。
Android 10 及更高版本
Android 10 及更高版本使用帶有任務設定檔的 cgroup:
- C組設定。開發人員在其
cgroups.json
檔案中描述 cgroup 設置,以定義 cgroup 集及其安裝位置和屬性。所有 cgroup 均在初始化過程的早期初始化階段掛載。 - 任務設定檔。它們提供了一種抽象,將所需的功能與其實現的細節分開。 Android 框架使用
SetTaskProfiles
和SetProcessProfiles
API 將task_profiles.json
檔案中所述的任務設定檔套用到進程或執行緒。 (這些 API 是 Android 11 及更高版本所獨有的。)
為了提供向後相容性,舊函數set_cpuset_policy
、 set_sched_policy
和get_sched_policy
提供相同的 API 和功能,但它們的實作已修改為使用任務設定檔。對於新用例,AOSP 建議使用新的任務設定檔 API,而不是舊的set_sched_policy
函數。
Cgroups描述文件
Cgroup 在位於<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
下的cgroups.json
檔案中進行了描述。每個控制器都在一個小節中進行描述,並且必須至少具有以下各項:
- 名稱,由Controller字段定義。
- 安裝路徑,由Path字段定義。
- Mode 、 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 及更高版本中,您可以定義一個 cgroup 控制器,透過將其設為true
,將路徑和模式指定為"Optional"
。
在早期初始化階段, cgroups.json
檔案作為 init 進程的一部分進行解析,並且 cgroup 被安裝在指定位置。如需稍後取得 cgroup 掛載位置,請使用CgroupGetControllerPath
API 函數。
任務設定檔
task_profiles.json
檔案位於<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
下。用它來描述要套用於流程或執行緒的一組特定操作。一組操作與設定檔名稱關聯,名稱在SetTaskProfiles
和SetProcessProfiles
呼叫中使用以呼叫設定檔操作。
範例 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 檔案作為屬性清單中的條目。每個條目包含以下內容:
- 名稱欄位指定屬性的名稱。
- Controller欄位透過名稱引用
cgroups.json
檔案中的 cgroup 控制器。 - 文件欄位命名此控制器下的特定文件。
屬性是任務設定檔定義中的引用。在任務設定檔之外,僅當框架需要直接存取這些檔案時才使用它們,並且無法使用任務設定檔來抽象存取權限。在所有其他情況下,使用任務設定檔;它們在所需行為及其實作細節之間提供了更好的解耦。
設定檔部分包含任務設定檔定義,其中包含以下內容:
- 名稱欄位定義設定檔名稱。
操作部分列出了應用設定檔時執行的一組操作。每個動作都有以下內容:
- 名稱欄位指定操作。
- Params部分指定操作的一組參數。
表中列出了支援的操作:
行動 | 範圍 | 描述 |
---|---|---|
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.json
和task_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系統會依照以下順序載入cgroup
和task_profile
檔:
- 預設
cgroups.json
和task_profiles.json
檔案。 - API 等級特定的檔案(如果存在)。
- 供應商分區文件(如果存在)。
對現有 API 的更改
Android 10 及更高版本保留函式set_cpuset_policy
、 set_sched_policy
和get_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) 將 profiles 套用到由執行緒 ID (tid) 使用其tid 參數指定的執行緒。 |
bool | SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) 將設定 profiles 套用到使用uid 和pid 參數的使用者和進程 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。 |