Android 10 以上版本會使用控制群組 (cgroup) 抽象層和工作設定檔,開發人員可用於描述要套用至執行緒或程序的限制集。然後,系統會按照工作設定檔的規定動作選取一或多個適當的 cgroup,藉此套用限制,並變更基礎 cgroup 功能集,不會影響較高的軟體層。
關於 cgroup
Cgroup 提供一種機制,可將一組工作 (包含程序、執行緒和所有未來的子項) 匯總及劃分為具有特殊行為的階層式群組。Android 會使用 cgroups 控制及計算 CPU 和記憶體用量與分配等系統資源,並支援 Linux 核心 cgroups v1 和 cgroups v2。
Android 9 以下版本
在 Android 9 以下版本中,init.rc
初始化指令碼包含一組可用的 Cgroup、掛接點和版本。雖然這些項目可以變更,但 Android 架構預期特定的一組 Cgroup 會存在於特定位置,並具有特定版本和子群組階層,這是根據指令碼而定。這項限制導致使用者無法選擇要使用的下一個 cgroup 版本,也無法變更 cgroup 階層來使用新功能。
Android 10 以上版本
Android 10 以上版本會使用 cgroup 和工作設定檔:
- 設定 Cgroup。開發人員會在
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
檔案中。每個控制器都會在子章節中說明,且至少須具備下列項目:
- 名稱,由「控制器」欄位定義。
- 掛接路徑,由「路徑」欄位定義。
- 模式、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/
下方。
可用於描述要套用至程序或執行緒的一組特定動作。一組動作會與設定檔名稱建立關聯,並用於 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 檔案做為項目。 每個項目都包含下列內容:
- 「名稱」欄位會指定屬性的名稱。
- 「控制器」欄位會依名稱參照
cgroups.json
檔案中的 cgroup 控制器。 - 「File」欄位會命名這個控制器下的特定檔案。
屬性是工作設定檔定義中的參照。除了工作設定檔之外,只有在架構需要直接存取這些檔案,且無法使用工作設定檔抽象化存取權時,才使用這些檔案。在所有其他情況下,請使用工作設定檔,因為這樣可更妥善地將必要行為與實作詳細資料分離。
「設定檔」部分包含工作設定檔定義,其中包含:
- 「名稱」欄位會定義設定檔名稱。
「動作」部分會列出套用設定檔時執行的一組動作。每項動作都有下列屬性:
- 「名稱」欄位會指定動作。
- 「Params」部分會指定動作的一組參數。
下表列出支援的動作:
動作 | 參數 | 說明 |
---|---|---|
SetTimerSlack |
Slack |
以 ns 為單位的計時器鬆弛時間 |
SetAttribute |
Name |
參照「屬性」部分中屬性的名稱 |
Value |
要寫入以具名屬性表示的檔案的值 | |
WriteFile | FilePath | 檔案路徑 |
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.json
和 task_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 系統會依下列順序載入 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
標頭。
使用任何這些函式的模組,都應在 Makefile 中新增 libprocessgroup
程式庫的依附元件。如果模組未使用任何其他 libcutils
功能,請從 Makefile 中捨棄 libcutils
程式庫依附元件。
工作設定檔 API
下表定義 processgroup/processgroup.h
中的私人 API:
類型 | API 和定義 |
---|---|
bool |
SetTaskProfiles(int tid, const std::vector
使用 tid 參數,將 profiles 中指定的工作設定檔套用至以執行緒 ID (tid) 指定的執行緒。 |
bool |
SetProcessProfiles(uid_t uid, pid_t pid, const std::vector
使用 uid 和 pid 參數,將 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。 |