Couche d'abstraction Cgroup

Android 10 et versions ultérieures utilisent une couche d'abstraction de groupe de contrôle (cgroup) avec des profils de tâche, que les développeurs peuvent utiliser pour décrire un ou plusieurs ensembles de restrictions à appliquer à un thread ou à un processus. Le système suit ensuite les actions prescrites des profils de tâche pour sélectionner un ou plusieurs cgroups appropriés, via lesquels des restrictions sont appliquées, et des modifications peuvent être apportées au jeu de fonctionnalités cgroup sous-jacent sans affecter les couches logicielles supérieures.

À propos des cgroups

Les groupes de contrôle fournissent un mécanisme permettant d'agréger et de partitionner des ensembles de tâches (qui consistent en des processus, des threads et tous leurs futurs enfants) en groupes hiérarchiques avec un comportement spécialisé. Android utilise des cgroups pour contrôler et comptabiliser les ressources système telles que l'utilisation et l'allocation du processeur et de la mémoire, avec la prise en charge des cgroups v1 et des cgroups v2 du noyau Linux.

Android 9 ou version antérieure

Sous Android 9 ou version antérieure, le script d'initialisation init.rc contenait l'ensemble des groupes de contrôle disponibles, leurs points d'installation et leurs versions. Bien que ces éléments puissent être modifiés, le framework Android s'attendait à ce qu'un ensemble spécifique de cgroups existe à des emplacements spécifiques avec une version et une hiérarchie de sous-groupes spécifiques, en fonction du script. Cela limitait la possibilité de choisir la prochaine version de cgroup à utiliser ou de modifier la hiérarchie cgroup pour utiliser de nouvelles fonctionnalités.

Android 10 ou version ultérieure

Android 10 et versions ultérieures utilisent des groupes de contrôle avec des profils de tâche:

  • Configuration du groupe C. Les développeurs décrivent la configuration des cgroups dans leur fichier cgroups.json pour définir des ensembles de cgroups, ainsi que leurs emplacements et attributs de montage. Tous les cgroups sont montés lors de la phase d'initialisation précoce du processus d'initialisation.
  • Profils de tâche Ils fournissent une abstraction qui dissocie la fonctionnalité requise des détails de son implémentation. Le framework Android applique les profils de tâche décrits dans le fichier task_profiles.json à un processus ou à un thread à l'aide des API SetTaskProfiles et SetProcessProfiles. (Ces API sont propres à Android 11 ou version ultérieure.)

Pour assurer la rétrocompatibilité, les anciennes fonctions set_cpuset_policy, set_sched_policy et get_sched_policy fournissent la même API et les mêmes fonctionnalités, mais leur implémentation a été modifiée pour utiliser des profils de tâche. Pour les nouveaux cas d'utilisation, AOSP recommande d'utiliser de nouvelles API de profils de tâche au lieu de l'ancienne fonction set_sched_policy.

Fichier de description des groupes de contrôle

Les groupes de contrôle sont décrits dans le fichier cgroups.json situé sous <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Chaque manette est décrite dans une sous-section et doit disposer au minimum des éléments suivants:

  • Nom, défini par le champ Contrôleur.
  • Chemin d'installation, défini par le champ Path (Chemin d'accès).
  • Mode, UID (ID utilisateur) et GID (ID de groupe) décrivant le propriétaire et les modes d'accès des fichiers sous ce chemin (tous facultatifs).
  • Attribut facultatif, défini sur true pour permettre au système d'ignorer l'erreur d'installation causée par un contrôleur cgroup que le noyau ne prend pas en charge.

Exemple de fichier cgroups.json

L'exemple ci-dessous montre les descriptions des contrôleurs cgroup v1 (Cgroups) et cgroup v2 (Cgroups2) avec leurs chemins respectifs.

{
  "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"
     }
   ]
 }
}

Cet exemple de fichier contient deux sections, Cgroups (décrivant les contrôleurs cgroup v1) et Cgroups2 (décrivant les contrôleurs cgroup v2). Tous les contrôleurs de la hiérarchie cgroups v2 sont installés au même emplacement. Par conséquent, la section Cgroups2 possède ses propres attributs Path, Mode, UID et GID pour décrire l'emplacement et les attributs de la racine de la hiérarchie. L'attribut Path (Chemin d'accès) des contrôleurs sous Cgroups2 est relatif à ce chemin d'accès racine. Dans Android 12 et versions ultérieures, vous pouvez définir un contrôleur cgroup spécifié avec le chemin d'accès et le mode "Optional" en le définissant sur true.

Le fichier cgroups.json est analysé dans le cadre du processus d'initialisation, lors de la phase d'initialisation précoce, et les cgroups sont montés aux emplacements spécifiés. Pour obtenir ultérieurement les emplacements de montage du cgroup, utilisez la fonction de l'API CgroupGetControllerPath.

Fichier de profils de tâches

Le fichier task_profiles.json se trouve sous <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Utilisez-le pour décrire un ensemble spécifique d'actions à appliquer à un processus ou à un thread. Un ensemble d'actions est associé à un nom de profil, qui est utilisé dans les appels SetTaskProfiles et SetProcessProfiles pour appeler les actions de profil.

Exemple de fichier 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" ]
     }
}

Attribuez un nom à des fichiers cgroup spécifiques dans la liste Attributs. Chaque entrée contient les éléments suivants:

  • Le champ Name (Nom) spécifie le nom de l'attribut.
  • Le champ Controller (Contrôleur) fait référence à un contrôleur cgroup à partir du fichier cgroups.json, par son nom.
  • Le champ File (Fichier) nomme un fichier spécifique de ce contrôleur.

Les attributs sont des références dans les définitions de profil de tâche. En dehors des profils de tâche, n'utilisez-les que lorsque le framework nécessite un accès direct à ces fichiers et que l'accès ne peut pas être abstrait à l'aide de profils de tâche. Dans tous les autres cas, utilisez des profils de tâche. Ils permettent un meilleur découplage entre le comportement requis et les détails de son implémentation.

La section Profils contient les définitions de profil de tâche suivantes:

  • Le champ Nom définit le nom du profil.
  • La section Actions liste un ensemble d'actions effectuées lorsque le profil est appliqué. Chaque action comporte les éléments suivants:

    • Le champ Name (Nom) spécifie l'action.
    • La section Params spécifie un ensemble de paramètres pour l'action.

Les actions compatibles sont répertoriées dans le tableau:

Action Paramètre Description
SetTimerSlack Slack Latence du minuteur en ns
SetAttribute Name Nom faisant référence à un attribut de la section Attributs
Value Valeur à écrire dans le fichier représenté par l'attribut nommé
WriteFileFilePathchemin d'accès au fichier
Valueune valeur à écrire dans le fichier
JoinCgroup Controller Nom du contrôleur cgroup à partir de cgroups.json
Path Chemin d'un sous-groupe dans la hiérarchie du contrôleur cgroup

Android 12 et versions ultérieures comportent une section AggregateProfiles contenant des profils agrégables, chacun étant un alias d'un ensemble d'un ou de plusieurs profils. Les définitions de profil agrégées se composent des éléments suivants:

  • Le champ Name (Nom) spécifie le nom du profil agrégé.
  • Le champ Profiles (Profils) liste les noms des profils inclus dans le profil agrégé.

Lorsqu'un profil agrégé est appliqué, tous les profils contenants sont également appliqués automatiquement. Les profils agrégables peuvent contenir à la fois des profils individuels et d'autres profils agrégables, à condition qu'il n'y ait pas de récursions (un profil qui s'inclut lui-même).

Commande init language task_profiles

Une commande task_profiles dans la langue d'initialisation Android est disponible pour Android 12 et versions ultérieures afin de faciliter l'activation du profil de tâche pour un processus spécifique. Il remplace la commande writepid (obsolète dans Android 12) qui était utilisée pour migrer un processus entre des groupes de contrôle. La commande task_profiles permet de modifier les implémentations sous-jacentes sans affecter les couches supérieures. Dans l'exemple ci-dessous, ces deux commandes effectuent effectivement la même opération:

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

    Obsolète dans Android 12, cette option permettait d'écrire le PID de la tâche en cours dans le fichier /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    Associe le processus en cours au groupe d'applications de premier plan sous le contrôleur "cpu" (cpuctl), ce qui entraîne l'écriture du PID du processus dans dev/cpuctl/top-app/tasks.

Utilisez toujours la commande task_profiles pour migrer des tâches dans des hiérarchies cgroup sous Android 12 ou version ultérieure. Il accepte un ou plusieurs paramètres représentant les noms des profils spécifiés dans le fichier task_profiles.json.

Par profil de tâche au niveau de l'API

Sous Android 12 et versions ultérieures, vous pouvez modifier ou remplacer les définitions dans les fichiers cgroups.json et task_profiles.json par défaut, en vous basant sur le niveau d'API Android ou en le faisant à partir de la partition du fournisseur.

Pour remplacer les définitions en fonction du niveau d'API, les fichiers suivants doivent être présents sur l'appareil:

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

    Utilisez-le pour les groupes de contrôle spécifiques à un niveau d'API.

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

    Utilisez-le pour les profils spécifiques à un niveau d'API.

Pour remplacer les définitions de la partition du fournisseur, les fichiers suivants doivent être présents sur l'appareil:

  • /vendor/etc/cgroups.json
  • /vendor/etc/task_profiles.json

Si un attribut ou une définition de profil dans ces fichiers porte le même nom que celui du fichier par défaut, la définition du fichier (au niveau de l'API ou du fournisseur) remplace la définition précédente. Notez également que les définitions au niveau du fournisseur remplacent celles au niveau de l'API. Si la nouvelle définition a un nouveau nom, l'ensemble d'attributs ou de profils est modifié avec la nouvelle définition.

Le système Android charge les fichiers cgroup et task_profile dans l'ordre suivant:

  1. Fichiers cgroups.json et task_profiles.json par défaut.
  2. Fichiers spécifiques au niveau de l'API, le cas échéant.
  3. Fichiers de partitionnement du fournisseur, le cas échéant.

Modifications apportées à l'API existante

Android 10 et versions ultérieures conservent les fonctions set_cpuset_policy, set_sched_policy et get_sched_policy sans modifier l'API. Toutefois, Android 10 déplace ces fonctions dans libprocessgroup, qui contient désormais toutes les fonctionnalités liées aux cgroup.

Bien que l'en-tête cutils/sched_policy.h existe toujours, pour éviter de casser le code existant, assurez-vous que le nouveau code inclut un nouvel en-tête processgroup/sched_policy.h à la place.

Les modules qui utilisent l'une de ces fonctions doivent ajouter une dépendance à la bibliothèque libprocessgroup dans leur fichier make. Si un module n'utilise aucune autre fonctionnalité libcutils, supprimez la dépendance de la bibliothèque libcutils du fichier makefile.

API de profils de tâche

Les API privées de processgroup/processgroup.h sont définies dans le tableau suivant:

Type API et définition
bool SetTaskProfiles(int tid, const std::vector& profiles)
Applique les profils de tâche spécifiés dans profiles au thread spécifié par un ID de thread (tid) à l'aide de son paramètre tid.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Applique les profils de tâches spécifiés dans profiles au processus spécifié par ses ID d'utilisateur et de processus à l'aide des paramètres uid et pid.
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Indique si un contrôleur cgroup spécifié par cgroup_name existe. Si la valeur est true, la variable path est définie sur la racine de ce cgroup.
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
Indique si un attribut de profil spécifié par attr_name existe. Si true, définit la variable path sur le chemin d'accès du fichier associé à cet attribut de profil.
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
Indique si un attribut de profil spécifié par attr_name existe. Si true, définit la variable path sur le chemin d'accès du fichier associé à cet attribut de profil et au thread spécifié par son ID de thread à l'aide du paramètre tid.
bool UsePerAppMemcg()
Indique si le système est configuré pour utiliser des cgroups de mémoire par application.