Уровень абстракции cgroup

В Android 10 и более поздних версиях используется абстрактный уровень группы управления (cgroup) с профилями задач, с помощью которых разработчики могут описывать наборы ограничений, применяемых к потоку или процессу. Затем система выполняет действия, описанные в профилях задач, чтобы выбрать одну или несколько подходящих групп cgroup, через которые применяются ограничения. При этом изменения в базовом наборе функций cgroup не влияют на более высокие уровни ПО.

О группах контроля

Cgroups позволяют объединять и разделять наборы задач (состоящие из процессов, потоков и всех их дочерних элементов) на иерархические группы со специальным поведением. Android использует cgroups для контроля и учета системных ресурсов, таких как использование и выделение ЦП и памяти, с поддержкой ядра Linux cgroups v1 и cgroups v2.

Android 9 и более ранних версий

В Android 9 и более ранних версиях скрипт инициализации init.rc содержал набор доступных групп cgroup, их точки подключения и версии. Хотя их можно было изменить, фреймворк Android ожидал, что определенный набор cgroup будет существовать в определенных местах с определенной версией и иерархией подгрупп на основе скрипта. Это ограничивало возможность выбора следующей версии cgroup или изменения иерархии cgroup для использования новых функций.

Android 10 и более поздних версий

В Android 10 и более поздних версий используются группы управления с профилями задач:

  • Настройка групп управления ресурсами. Разработчики описывают настройку cgroups в файле cgroups.json, чтобы определить наборы cgroups, а также их точки подключения и атрибуты. Все cgroups монтируются на раннем этапе инициализации.
  • Профили задач. Они представляют собой абстракцию, которая отделяет необходимую функциональность от деталей ее реализации. Фреймворк Android применяет профили задач, описанные в файле task_profiles.json, к процессу или потоку с помощью API SetTaskProfiles и SetProcessProfiles. (Эти API доступны только в Android 11 и более поздних версиях.)

Для обеспечения обратной совместимости устаревшие функции set_cpuset_policy, set_sched_policy и get_sched_policy предоставляют тот же API и функциональность, но их реализация была изменена для использования профилей задач. В новых случаях использования AOSP рекомендует применять новые API профилей задач вместо устаревшей функции set_sched_policy.

Файл описания cgroups

Группы управления ресурсами описаны в файле cgroups.json, который находится в каталоге <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Каждый контроллер описан в отдельном подразделе и должен содержать как минимум следующие сведения:

  • Название, указанное в поле Контроллер.
  • Путь монтирования, заданный в поле Путь.
  • Режим, UID (идентификатор пользователя) и GID (идентификатор группы), описывающие владельца и режимы доступа для файлов по этому пути (все необязательные).
  • Необязательный атрибут. Если задать для него значение 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 версии 1) и Cgroups2 (описывает контроллеры cgroup версии 2). Все контроллеры в иерархии cgroups v2 монтируются в одном и том же месте. Поэтому в разделе Cgroups2 есть собственные атрибуты Path, Mode, UID и GID, описывающие местоположение и атрибуты для корня иерархии. Атрибут Path для Controllers в Cgroups2 относится к этому корневому пути. В Android 12 и более поздних версий можно задать контроллер cgroup, указанный с помощью пути и режима "Optional", установив для него значение true.

Файл cgroups.json анализируется в рамках процесса инициализации на этапе ранней инициализации, и контрольные группы монтируются в указанных местах. Чтобы получить точки подключения cgroup, используйте функцию API CgroupGetControllerPath.

Файл профилей задач

Файл 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 содержит название контроллера cgroup из файла cgroups.json.
  • Поле File (Файл) содержит название определенного файла, относящегося к этому контролеру.

Атрибуты – это ссылки в определениях профилей задач. Вне профилей задач используйте их только в том случае, если фреймворку требуется прямой доступ к этим файлам и доступ нельзя абстрагировать с помощью профилей задач. Во всех остальных случаях используйте профили задач, поскольку они обеспечивают лучшее разделение между требуемым поведением и деталями его реализации.

В разделе Профили содержатся определения профилей задач со следующими элементами:

  • В поле Название задается название профиля.
  • В разделе Действия перечислены действия, которые выполняются при применении профиля. У каждого действия есть:

    • В поле Название указывается действие.
    • В разделе Params указан набор параметров для действия.

Поддерживаемые действия перечислены в таблице ниже.

Действие Параметр Описание
SetTimerSlack Slack Задержка таймера в наносекундах
SetAttribute Name Название, относящееся к атрибуту из раздела Атрибуты.
Value Значение, которое будет записано в файл, представленный именованным атрибутом.
WriteFileFilePathпуть к файлу;
Valueзначение, которое нужно записать в файл.
JoinCgroup Controller Название контроллера cgroup из cgroups.json.
Path Путь к подгруппе в иерархии контроллера cgroup

В Android 12 и более поздних версий есть раздел AggregateProfiles, в котором содержатся агрегированные профили. Каждый из них представляет собой псевдоним для набора из одного или нескольких профилей. Определения агрегированных профилей состоят из следующих элементов:

  • В поле Название указывается название агрегированного профиля.
  • В поле Профили перечислены названия профилей, включенных в агрегированный профиль.

При применении агрегированного профиля все входящие в него профили также применяются автоматически. Агрегированные профили могут содержать как отдельные профили, так и другие агрегированные профили, если нет рекурсий (профиль, который включает сам себя).

task_profiles init language command

В Android 12 и более поздних версиях ОС для активации профиля задачи для определенного процесса доступна команда task_profiles в языке инициализации Android. Она заменяет команду writepid (не поддерживается в Android 12), которая использовалась для переноса процесса между группами управления. Команда task_profiles позволяет изменять базовые реализации, не затрагивая верхние уровни. В приведенном ниже примере обе команды выполняют одну и ту же операцию:

  • writepid /dev/cpuctl/top-app/tasks

    Устарело в Android 12. Ранее использовалось для записи PID текущей задачи в файл /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    Добавляет текущий процесс в группу приложений, которые больше всего нагружают процессор, в контроллере cpu (cpuctl). В результате идентификатор процесса записывается в dev/cpuctl/top-app/tasks.

Всегда используйте команду task_profiles для переноса задач в иерархиях cgroup в Android 12 и более поздних версий. Он принимает один или несколько параметров, представляющих названия профилей, указанных в файле task_profiles.json.

Профили задач для каждого уровня API

В Android 12 и более поздних версиях вы можете изменять или переопределять определения в файлах cgroups.json и task_profiles.json по умолчанию, основываясь на уровне API Android или используя раздел поставщика.

Чтобы переопределить значения на основе уровня API, на устройстве должны быть следующие файлы:

  • /system/etc/task_profiles/cgroups_<API level>.json

    Используйте это для cgroups, относящихся к уровню API.

  • /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 в следующем порядке:

  1. Файлы cgroups.json и task_profiles.json по умолчанию.
  2. файлы, относящиеся к определенному уровню API (если есть).
  3. Файлы сегмента поставщика (если есть).

Изменения в существующих API

В Android 10 и более поздних версий функции set_cpuset_policy, set_sched_policy и get_sched_policy не изменились. Однако в Android 10 эти функции перенесены в каталог libprocessgroup, который теперь содержит все функции, связанные с cgroup.

Заголовок cutils/sched_policy.h по-прежнему существует, но, чтобы не нарушать работу существующего кода, убедитесь, что новый код включает заголовок processgroup/sched_policy.h.

Модули, в которых используются какие-либо из этих функций, должны добавить в свой файл makefile зависимость от библиотеки libprocessgroup. Если модуль не использует другие функции libcutils, удалите зависимость библиотеки libcutils из файла makefile.

API профилей задач

Закрытые API в processgroup/processgroup.h определены в таблице:

Тип API и определение
bool SetTaskProfiles(int tid, const std::vector& profiles)
Применяет профили задач, указанные в profiles, к потоку, заданному идентификатором потока (tid) с помощью параметра tid.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Применяет профили задач, указанные в profiles, к процессу, заданному с помощью идентификаторов пользователя и процесса в параметрах uid и pid.
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 присваивается путь к файлу, связанному с этим атрибутом профиля, а также к потоку, заданному идентификатором потока с помощью параметра tid.
bool UsePerAppMemcg()
Возвращает информацию о том, настроена ли система на использование cgroup памяти для каждого приложения.