Android 10 以上版本會使用控制群組 (cgroup) 抽象層,並搭配任務設定檔,開發人員可用來描述要套用至執行緒或程序的一組 (或多組) 限制。系統會依照工作設定檔的規定動作,選取一或多個適當的 cgroup,並透過這些 cgroup 套用限制,進而變更基礎 cgroup 功能集,而不會影響較高層級的軟體層。
關於 cgroups
Cgroup 提供一種機制,可將一組工作 (包含程序、執行緒和所有未來子項) 匯總及分割為具有特殊行為的階層群組。Android 使用 cgroups 控管及處理系統資源 (例如 CPU 和記憶體用量和分配),並支援 Linux 核心 cgroups v1 和 cgroups v2。
Android 9 以下版本
在 Android 9 以下版本中,init.rc
初始化指令碼包含可用的 cgroups 集合、掛載點和版本。雖然這些項目可以變更,但 Android 架構會根據指令碼,預期特定位置會存在特定的 cgroup 組合,並且具有特定版本和子群組階層。這會限制您選擇下一個要使用的 cgroup 版本,或變更 cgroup 階層以使用新功能的功能。
Android 10 以上版本
Android 10 以上版本會使用具有工作設定檔的 cgroups:
- 群組設定。開發人員會在
cgroups.json
檔案中說明 cgroup 設定,以定義 cgroup 組合、其掛載位置和屬性。所有 cgroups 都會在初始化程序的早期初始化階段掛接。 - 工作設定檔。這些抽象類別可將所需功能與其實作細節分開。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 說明檔案
在 <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
底下的 cgroups.json
檔案中,會說明 C 群組。每個控制器都會在子區段中說明,且必須至少包含下列項目:
- 名稱,由「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 底下「Controllers」的「Path」屬性會相對於該根路徑。在 Android 12 以上版本中,您可以定義 cgroup 控制器,並將路徑和模式指定為 "Optional"
,方法是將其設為 true
。
在初始化階段的早期,系統會在初始化程序中剖析 cgroups.json
檔案,並在指定位置掛載 cgroups。如要日後取得 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 檔案指派名稱,做為「Attributes」清單中的項目。每個項目都包含下列項目:
- 「名稱」欄位會指定屬性名稱。
- 「Controller」欄位會根據名稱參照
cgroups.json
檔案中的 cgroup 控制器。 - 「File」欄位會命名這個控制器底下的特定檔案。
「屬性」是工作設定檔定義中的參照。除了工作設定檔之外,只有在架構需要直接存取這些檔案,且無法使用工作設定檔抽象化存取權時,才使用工作設定檔。在所有其他情況下,請使用工作設定檔,因為這類設定檔可在必要行為與其實作細節之間提供更佳的解耦。
「Profiles」部分包含工作資料夾定義,其中包含以下內容:
- 「Name」欄位定義設定檔名稱。
「Actions」(動作) 區段會列出套用設定檔時要執行的一組動作。每個動作都包含以下項目:
- 「Name」欄位會指定動作。
- 「Params」部分會指定一組操作參數。
支援的動作列於下表:
動作 | 參數 | 說明 |
---|---|---|
SetTimerSlack |
Slack |
計時器鬆弛時間 (以奈秒為單位) |
SetAttribute |
Name |
參照「Attributes」區段中屬性的名稱 |
Value |
要寫入以指定屬性表示檔案的值 | |
WriteFile | FilePath | 檔案路徑 |
Value | 要寫入檔案的值 | |
JoinCgroup |
Controller |
cgroups.json 中的 cgroup 控制器名稱 |
Path |
cgroup 控制器階層中的子群組路徑 |
Android 12 以上版本提供「AggregateProfiles」區段,其中包含匯總設定檔,每個匯總設定檔都是一或多個設定檔的別名。匯總設定檔定義包含下列項目:
- 「名稱」欄位會指定匯總設定檔的名稱。
- 「Profiles」欄位會列出匯總設定檔中包含的設定檔名稱。
套用匯總設定檔時,系統也會自動套用所有包含的設定檔。匯總設定檔可包含個別設定檔或其他匯總設定檔,但不得有遞迴 (包含自身的設定檔)。
task_profiles 初始化語言指令
Android Init Language 中的 task_profiles
指令適用於 Android 12 以上版本,可為特定程序啟用工作設定檔。這會取代用來在 cgroups 之間遷移程序的 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 以上版本中,您可以修改或覆寫預設 cgroups.json
和 task_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 系統會按照下列順序載入 cgroup
和 task_profile
檔案:
- 預設的
cgroups.json
和task_profiles.json
檔案。 - API 級別專屬檔案 (如果有)。
- 廠商分區檔案 (如有)。
現有 API 的變更
Android 10 以上版本可在不變更 API 的情況下保留 set_cpuset_policy
、set_sched_policy
和 get_sched_policy
函式。不過,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 參數指定的執行緒 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 指定的設定檔屬性是否存在;如果存在,true 會將 path 變數設為與該設定檔屬性相關聯的檔案路徑。 |
bool |
CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
會傳回 attr_name 指定的設定檔屬性是否存在;如果 true ,則會將 path 變數設為與該設定檔屬性相關聯的檔案路徑,以及使用 tid 參數指定的執行緒 ID 所指定的執行緒。 |
bool |
UsePerAppMemcg()
傳回系統是否設為使用個別應用程式記憶體 cgroups。 |