Cgroup抽象層

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

關於 cgroup

Cgroups 提供了一種將任務集(由進程、線程及其所有未來子項組成)聚合和劃分為具有特定行為的分層組的機制。 Android使用的cgroup以控制和佔系統資源,如CPU和內存的使用和分配,與Linux內核支持的cgroup V1cgroup的V2

Android 9 及更低版本

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

Android 10 及更高版本

Android 10 及更高版本將 cgroup 與任務配置文件一起使用:

  • CGROUP設置-開發商描述的cgroup設置在其cgroups.json文件來定義組的cgroup,他們的安裝位置和屬性。在初始化過程的早期初始化階段安裝所有 cgroup。
  • 任務配置文件 - 這些提供了一種抽象,將所需的功能與其實現的細節分離。而Android應用框架中所描述的任務剖面task_profiles.json使用該文件的進程或線程SetTaskProfilesSetProcessProfiles的API。 (這些 API 是 Android 11 及更高版本獨有的。)

為了提供向後兼容性,遺留功能set_cpuset_policyset_sched_policyget_sched_policy提供相同的API和功能,但它們的實現已經被修改為使用任務文件。對於新的使用案例AOSP建議使用新的任務剖面的API,而不是傳統set_sched_policy功能。

Cgroups 描述文件

cgroup中在描述cgroups.json位於下文件<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ 。每個控制器都在一個小節中進行了描述,並且必須至少具有以下內容:

  • 名,由控制器領域的定義。
  • 安裝路徑上,由路徑字段定義。
  • 模式,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"
     }
   ]
 }
}

此示例文件包含兩部分,cgroup中(描述該cgroup V1控制器)和Cgroups2(描述該cgroup V2控制器)。 cgroups v2 層次結構中的所有控制器都安裝在同一位置。因此,Cgroups2節都有自己的路徑模式,UIDGID屬性來描述的位置和層次結構的根本屬性。對於Cgroups2控制器路徑屬性是相對於該根路徑。在Android中12及更高您可以定義一個與路徑和模式指定的cgroup控制器"Optional"將其設置為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的文件,在你的屬性列表中的條目。每個條目包含以下內容:

  • 名稱字段-指定屬性的名稱。
  • 控制器領域-引用從cgroup中控制器cgroups.json文件,它的名字。
  • 文件字段-名該控制器在一個特定的文件。

屬性是在任務配置文件定義引用。任務配置文件以外的,使用它們只有當框架需要對這些文件的直接訪問,和訪問不能使用任務輪廓抽象。在所有其他情況下,使用任務配置文件;它們在所需的行為與其實現細節之間提供了更好的解耦。

配置文件部分包含以下任務配置文件定義:

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

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

下表列出了支持的操作。

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

機器人12和更高的特徵的AggregateProfiles節,其中包含聚合型材,其中的每一個是一組中的一個或多個簡檔的一個別名。聚合配置文件定義包括以下內容:

  • 名稱字段-指定骨料配置文件的名稱。
  • 型材領域-包括在總型材型材名單上的名字。

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

task_profiles 初始化語言命令

一個task_profiles在命令Android的初始化語言是可用於Android 12和更高便利任務配置文件激活特定的進程。它取代了writepid的是使用遷移的cgroup之間的處理命令(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

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

機器人10和更高的保持功能set_cpuset_policyset_sched_policyget_sched_policy無需改動API。不過,Android 10移動這些功能到libprocessgroup ,它現在包含了所有的cgroup相關的功能。

雖然cutils/sched_policy.h頭仍然存在,以避免破壞現有代碼確保新的代碼包括一個新的processgroup/sched_policy.h頭而不是。

使用任何一個功能模塊應該添加依賴於libprocessgroup庫到他們的makefile文件。如果一個模塊不使用任何其他libcutils功能,刪除libcutils庫的依賴從makefile文件。

任務配置文件 API

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

類型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 ,以利用其用戶和進程ID指定的進程uidpid參數

bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)

返回由指定的cgroup中控制器是否cgroup_name存在;如果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變量到與該簡檔屬性相關聯的文件的路徑,以及通過使用它的線程ID指定的線程tid參數。

bool UsePerAppMemcg()

返回系統是否配置為使用每個應用程序的內存 cgroup。