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

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

О группах cgroups

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

Android 9 и ниже

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

Android 10 и выше

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

  • Настройка cgroup. Разработчики описывают настройку 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 описываются в файле cgroups.json , расположенном в каталоге <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ . Каждый контроллер описывается в подразделе и должен иметь как минимум следующие параметры:

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

Файл cgroups.json анализируется в процессе инициализации, на ранней стадии инициализации, и группы cgroup монтируются в указанные места. Для получения мест монтирования групп 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 по его имени.
  • Поле «Файл» указывает имя конкретного файла в рамках данного контроллера.

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

В разделе «Профили» представлены определения профилей задач со следующими характеристиками:

  • Поле «Имя» определяет имя профиля.
  • В разделе «Действия» перечислены действия, выполняемые при применении профиля. Каждое действие включает в себя следующее:

    • В поле «Имя» указывается действие.
    • В разделе «Параметры» задается набор параметров для выполнения действия.

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

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

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

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

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

task_profiles init language command

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

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

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

  • task_profiles MaxPerformance

    Присоединяет текущий процесс к группе top-app в контроллере "cpu" ( cpuctl ), в результате чего PID процесса записывается в файл dev/cpuctl/top-app/tasks .

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

Профили задач на уровне API

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

Для переопределения определений на уровне 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 сохранены без изменений в 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)
Применяет профили задач, указанные в 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()
Возвращает значение, указывающее, настроена ли система на использование групп памяти для каждого приложения.