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âches, 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âches pour sélectionner un ou plusieurs groupes de contrôle appropriés, par l'intermédiaire desquels des restrictions sont appliquées, et des modifications de l'ensemble de fonctionnalités de groupe de contrôle sous-jacent peuvent être apportées sans affecter les couches logicielles supérieures.

À propos des groupes de contrôle

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

Android 9 et versions antérieures

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

Android 10 et supérieur

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

  • Configuration du groupe de contrôle : les développeurs décrivent la configuration des groupes de contrôle dans leur fichier cgroups.json pour définir des ensembles de groupes de contrôle, ainsi que leurs emplacements et attributs de montage. Tous les groupes de contrôle sont montés au début de la phase d'initialisation.
  • Profils de tâches : ils fournissent une abstraction qui dissocie la fonctionnalité requise des détails de sa mise en œuvre. Le framework Android applique les profils de tâches comme décrit dans le fichier task_profiles.json à un processus ou un thread à l'aide des API SetTaskProfiles et SetProcessProfiles . (Ces API sont uniques à Android 11 et versions ultérieures.)

Pour assurer la compatibilité descendante, les fonctions héritées 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âches. Pour les nouveaux cas d'utilisation, l'AOSP recommande d'utiliser de nouvelles API de profils de tâches au lieu de l'ancienne fonction set_sched_policy .

Fichier de description des groupes C

Les groupes C sont décrits dans le fichier cgroups.json situé sous <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ . Chaque contrôleur est décrit dans une sous-section et doit posséder au minimum les éléments suivants :

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

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

Ce fichier d'exemple 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 monté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 pour les contrôleurs sous Cgroups2 est relatif à ce chemin racine. Sous Android 12 et versions ultérieures, vous pouvez définir un contrôleur de groupe de contrôle spécifié avec le chemin et le mode comme "Optional" en le définissant sur true .

Le fichier cgroups.json est analysé dans le cadre du processus d'initialisation, au cours de la première étape d'initialisation, et les groupes de contrôle sont montés aux emplacements spécifiés. Pour obtenir ultérieurement les emplacements de montage du groupe de contrôle, utilisez la fonction 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 des 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 des noms à des fichiers de groupe de contrôle spécifiques en tant qu'entrées dans votre liste d'attributs . Chaque entrée contient les éléments suivants :

  • Champ Nom : spécifie le nom de l'attribut.
  • Champ Contrôleur : fait référence à un contrôleur de groupe de contrôle à partir du fichier cgroups.json , par son nom.
  • Champ Fichier - nomme un fichier spécifique sous 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âches, utilisez-les uniquement 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âches. Dans tous les autres cas, utilisez des profils de tâches ; ils offrent un meilleur découplage entre le comportement requis et les détails de sa mise en œuvre.

La section Profils contient les définitions de profils de tâches avec les éléments suivants :

  • Champ Nom - définit le nom du profil.
  • Section Actions - répertorie un ensemble d'actions effectuées lorsque le profil est appliqué. Chaque action comporte les éléments suivants :

    • Champ de nom spécifiant l'action
    • Section Params spécifiant un ensemble de paramètres pour l'action

Les actions prises en charge sont répertoriées dans le tableau ci-dessous.

Action Paramètre Description
SetTimerSlack Slack Relâchement du timer en ns
SetAttribute Name Un nom faisant référence à un attribut de la section Attributs
Value Une valeur à écrire dans le fichier représenté par l'attribut nommé
WriteFile FilePath chemin d'accès au fichier
Value une valeur à écrire dans le fichier
JoinCgroup Controller Un nom du contrôleur de groupe de contrôle de cgroups.json
Path Un chemin de sous-groupe dans la hiérarchie du contrôleur de groupe de contrôle

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

  • Champ Nom : spécifie le nom du profil agrégé.
  • Champ Profils - répertorie les noms des profils inclus dans le profil agrégé.

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

commande de langage d'initialisation task_profiles

Une commande task_profiles dans le langage Android Init est disponible pour Android 12 et versions ultérieures pour faciliter l'activation du profil de tâche pour un processus spécifique. Elle remplace la commande writepid (obsolète dans Android 12) qui était utilisée pour migrer un processus entre les groupes de contrôle. La commande task_profiles offre une certaine flexibilité pour modifier les implémentations sous-jacentes sans effet sur 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 - A été utilisé pour écrire le PID de la tâche actuelle dans le fichier /dev/cpuctl/top-app/tasks .

  • task_profiles MaxPerformance

    Rejoint le processus actuel dans le groupe top-app 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 les tâches dans les hiérarchies de groupe de contrôle dans Android 12 et versions ultérieures. Il accepte un ou plusieurs paramètres, représentant les noms des profils spécifiés dans le fichier task_profiles.json .

Par profils de tâches 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, soit en basant votre modification sur le niveau de l'API Android, soit en l'effectuant à partir de la partition du fournisseur.

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

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

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

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

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

Pour remplacer les définitions de la partition fournisseur, les fichiers suivants doivent être présents sur le périphérique :

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

Si un attribut ou une définition de profil dans ces fichiers utilise 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 les définitions au niveau de l'API. Si la nouvelle définition porte un nouveau nom, alors 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 cet ordre :

  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 partition du fournisseur, le cas échéant.

Modifications de l'API existante

Android 10 et versions ultérieures conservent les fonctions set_cpuset_policy , set_sched_policy et get_sched_policy sans modification de l'API. Cependant, Android 10 déplace ces fonctions vers libprocessgroup , qui contient désormais toutes les fonctionnalités liées au groupe de contrôle.

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 plutôt un nouvel en-tête processgroup/sched_policy.h .

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

API de profils de tâches

Les API privées dans processgroup/processgroup.h sont définies dans le tableau ci-dessous :

Taper API et définition
bool SetTaskProfiles(int tid, const std::vector & profiles) SetTaskProfiles(int tid, const std::vector & profiles)

Applique les profils de tâches spécifiés dans les 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) 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 son utilisateur et ses ID de processus à l'aide des paramètres uid et pid

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

Renvoie si un contrôleur de groupe de contrôle spécifié par cgroup_name existe ; si true , définit la variable path à la racine de ce groupe de contrôle

bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)

Renvoie si un attribut de profil spécifié par attr_name existe ; si true , définit la variable path sur le chemin du fichier associé à cet attribut de profil.

bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)

Renvoie si un attribut de profil spécifié par attr_name existe ; si true , définit la variable path sur le chemin du fichier associé à cet attribut de profil et sur le 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 groupes de contrôle de mémoire par application.