Cgroup-Abstraktionsschicht

In Android 10 und höher wird eine Abstraktionsebene für Kontrollgruppen (cgroups) mit Aufgabenprofilen verwendet, mit der Entwickler eine oder mehrere Einschränkungen für einen Thread oder Prozess beschreiben können. Das System folgt dann den vorgeschriebenen Aktionen der Aufgabenprofile, um eine oder mehrere geeignete Cgroups auszuwählen, über die Einschränkungen angewendet werden. Änderungen am zugrunde liegenden Cgroup-Funktionssatz können vorgenommen werden, ohne dass höhere Softwareebenen beeinträchtigt werden.

Cgroups

Cgroups bieten einen Mechanismus zum Aggregieren und Partitionieren von Aufgabengruppen (die aus Prozessen, Threads und allen zukünftigen untergeordneten Elementen bestehen) in hierarchische Gruppen mit spezialisiertem Verhalten. Android verwendet Cgroups, um Systemressourcen wie CPU- und Arbeitsspeicherverbrauch und -zuweisung zu steuern und zu erfassen. Dabei werden Cgroups v1 und Cgroups v2 des Linux-Kernels unterstützt.

Android 9 und niedriger

In Android 9 und niedriger enthielt das Initialisierungsskript init.rc die verfügbaren Cgroups, ihre Mount-Punkte und Versionen. Diese konnten zwar geändert werden, aber das Android-Framework erwartete, dass basierend auf dem Skript eine bestimmte Gruppe von Cgroups an bestimmten Speicherorten mit einer bestimmten Version und Untergruppenhierarchie vorhanden war. Dadurch war es nur eingeschränkt möglich, die nächste zu verwendende Cgroup-Version auszuwählen oder die Cgroup-Hierarchie zu ändern, um neue Funktionen zu nutzen.

Android 10 und höher

Unter Android 10 und höher werden Cgroups mit Aufgabenprofilen verwendet:

  • Cgroup-Einrichtung. Entwickler beschreiben die Einrichtung von Cgroups in ihrer cgroups.json-Datei, um Gruppen von Cgroups sowie ihre Mount-Punkte und Attribute zu definieren. Alle Cgroups werden in der Early-Init-Phase des Initialisierungsprozesses eingebunden.
  • Aufgabenprofile: Sie bieten eine Abstraktion, die die erforderliche Funktionalität von den Details ihrer Implementierung entkoppelt. Das Android-Framework wendet die Aufgabenprofile wie in der task_profiles.json-Datei beschrieben auf einen Prozess oder Thread an. Dazu werden die APIs SetTaskProfiles und SetProcessProfiles verwendet. Diese APIs sind nur unter Android 11 und höher verfügbar.

Aus Gründen der Abwärtskompatibilität bieten die Legacy-Funktionen set_cpuset_policy, set_sched_policy und get_sched_policy dieselbe API und Funktionalität, ihre Implementierung wurde jedoch so geändert, dass Aufgabenprofile verwendet werden. Für neue Anwendungsfälle empfiehlt AOSP, anstelle der alten set_sched_policy-Funktion die neuen APIs für Aufgabenprofile zu verwenden.

Cgroups-Beschreibungsdatei

Cgroups werden in der Datei cgroups.json unter <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ beschrieben. Jeder Controller wird in einem Unterabschnitt beschrieben und muss mindestens Folgendes enthalten:

  • Name, der durch das Feld Controller definiert wird.
  • Bereitstellungspfad, der durch das Feld Pfad definiert wird.
  • Mode, UID (Nutzer-ID) und GID (Gruppen-ID) zur Beschreibung des Inhabers und der Zugriffsmodi für die Dateien unter diesem Pfad (alle optional).
  • Optionales Attribut, das auf true gesetzt wird, damit das System den Bereitstellungsfehler ignoriert, der durch einen Cgroup-Controller verursacht wird, dessen Bereitstellung vom Kernel nicht unterstützt wird.

Beispieldatei „cgroups.json“

Im folgenden Beispiel werden Beschreibungen für cgroup v1-Controller (Cgroups) und cgroup v2-Controller (Cgroups2) mit den jeweiligen Pfaden angezeigt.

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

Diese Beispieldatei enthält zwei Abschnitte: Cgroups (zur Beschreibung der cgroup v1-Controller) und Cgroups2 (zur Beschreibung der cgroup v2-Controller). Alle Controller in der cgroups v2-Hierarchie werden am selben Ort eingebunden. Daher hat der Abschnitt Cgroups2 eigene Attribute für Path, Mode, UID und GID, um den Speicherort und die Attribute für den Stamm der Hierarchie zu beschreiben. Das Attribut Path für Controllers unter Cgroups2 ist relativ zu diesem Stammverzeichnis. In Android 12 und höher können Sie einen Cgroup-Controller definieren, der mit Pfad und Modus als "Optional" angegeben wird, indem Sie ihn auf true festlegen.

Die Datei cgroups.json wird im Rahmen des Initialisierungsprozesses in der Phase „early-init“ geparst und die Cgroups werden an den angegebenen Speicherorten bereitgestellt. Verwenden Sie die API-Funktion CgroupGetControllerPath, um später die Mount-Punkte der Cgroup abzurufen.

Datei mit Taskprofilen

Die Datei task_profiles.json befindet sich unter <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Damit wird eine bestimmte Reihe von Aktionen beschrieben, die auf einen Prozess oder einen Thread angewendet werden sollen. Einem Profilnamen ist eine Reihe von Aktionen zugeordnet, die in SetTaskProfiles- und SetProcessProfiles-Aufrufen verwendet werden, um Profilaktionen aufzurufen.

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

Weisen Sie bestimmten cgroup-Dateien Namen als Einträge in der Liste Attribute zu. Jeder Eintrag enthält Folgendes:

  • Im Feld Name wird der Name des Attributs angegeben.
  • Das Feld Controller verweist anhand des Namens auf einen Cgroup-Controller aus der Datei cgroups.json.
  • Im Feld File wird eine bestimmte Datei unter diesem Controller angegeben.

Attribute sind Referenzen in Definitionen von Aufgabenprofilen. Außerhalb von Aufgabenprofilen sollten Sie sie nur verwenden, wenn das Framework direkten Zugriff auf diese Dateien erfordert und der Zugriff nicht mithilfe von Aufgabenprofilen abstrahiert werden kann. In allen anderen Fällen sollten Sie Aufgabenprofile verwenden, da sie eine bessere Entkopplung zwischen dem erforderlichen Verhalten und den Implementierungsdetails ermöglichen.

Der Bereich Profile enthält Definitionen von Aufgabenprofilen mit den folgenden Elementen:

  • Im Feld Name wird der Profilname definiert.
  • Im Bereich Aktionen ist eine Reihe von Aktionen aufgeführt, die ausgeführt werden, wenn das Profil angewendet wird. Jede Aktion hat Folgendes:

    • Im Feld Name wird die Aktion angegeben.
    • Im Abschnitt Params wird eine Reihe von Parametern für die Aktion angegeben.

Unterstützte Aktionen sind in der Tabelle aufgeführt:

Aktion Parameter Beschreibung
SetTimerSlack Slack Timer-Spielraum in Nanosekunden
SetAttribute Name Ein Name, der auf ein Attribut aus dem Abschnitt Attribute verweist
Value Ein Wert, der in die Datei geschrieben werden soll, die durch das benannte Attribut dargestellt wird.
WriteFileFilePathPfad zur Datei
ValueEin Wert, der in die Datei geschrieben werden soll
JoinCgroup Controller Ein Name des Cgroup-Controllers aus cgroups.json
Path Ein Untergruppenpfad in der Hierarchie des cgroup-Controllers

In Android 12 und höher gibt es den Abschnitt AggregateProfiles, der aggregierte Profile enthält. Jedes aggregierte Profil ist ein Alias für eine Gruppe von einem oder mehreren Profilen. Zusammengefasste Profildefinitionen bestehen aus den folgenden Elementen:

  • Im Feld Name wird der Name des aggregierten Profils angegeben.
  • Im Feld Profile sind die Namen der Profile aufgeführt, die im aggregierten Profil enthalten sind.

Wenn ein zusammengefasstes Profil angewendet wird, werden auch alle darin enthaltenen Profile automatisch angewendet. Aggregierte Profile können sowohl einzelne Profile als auch andere aggregierte Profile enthalten, sofern keine Rekursionen (ein Profil, das sich selbst enthält) auftreten.

Befehl „task_profiles init language“

Für Android 12 und höher ist ein task_profiles-Befehl in der Android Init Language verfügbar, um die Aktivierung von Aufgabenprofilen für einen bestimmten Prozess zu erleichtern. Es ersetzt den Befehl writepid (der in Android 12 eingestellt wurde), mit dem ein Prozess zwischen Cgroups migriert wurde. Der Befehl task_profiles bietet Flexibilität beim Ändern zugrunde liegender Implementierungen, ohne dass sich dies auf die oberen Ebenen auswirkt. Im folgenden Beispiel führen diese beiden Befehle im Grunde dieselbe Operation aus:

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

    In Android 12 eingestellt. Damit wurde die PID der aktuellen Aufgabe in die Datei /dev/cpuctl/top-app/tasks geschrieben.

  • task_profiles MaxPerformance

    Fügt den aktuellen Prozess in die Gruppe der Top-Apps unter dem Controller „cpu“ (cpuctl) ein. Dadurch wird die PID des Prozesses in dev/cpuctl/top-app/tasks geschrieben.

Verwenden Sie immer den Befehl task_profiles, um Aufgaben in Cgroup-Hierarchien in Android 12 und höher zu migrieren. Es akzeptiert einen oder mehrere Parameter, die die Namen der in der task_profiles.json-Datei angegebenen Profile darstellen.

Aufgabeprofile nach API-Level

In Android 12 und höher können Sie Definitionen in den Standarddateien cgroups.json und task_profiles.json ändern oder überschreiben. Dabei können Sie Ihre Änderung entweder auf der Android-API-Ebene basieren oder sie über die Anbieterpartition vornehmen.

Wenn Sie die Definitionen basierend auf der API-Ebene überschreiben möchten, müssen die folgenden Dateien auf dem Gerät vorhanden sein:

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

    Verwenden Sie dies für Cgroups, die für eine API-Ebene spezifisch sind.

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

    Verwenden Sie diese Option für Profile, die für eine bestimmte API-Ebene gelten.

Wenn Sie die Definitionen aus der Anbieterpartition überschreiben möchten, müssen die folgenden Dateien auf dem Gerät vorhanden sein:

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

Wenn ein Attribut oder eine Profildefinition in diesen Dateien denselben Namen wie in der Standarddatei verwendet, wird die vorherige Definition durch die Dateidefinition (API-Ebene oder Anbieterebene) überschrieben. Beachten Sie auch, dass Definitionen auf Anbieterebene Definitionen auf API-Ebene überschreiben. Wenn die neue Definition einen neuen Namen hat, wird die Gruppe von Attributen oder Profilen um die neue Definition erweitert.

Das Android-System lädt die Dateien cgroup und task_profile in dieser Reihenfolge:

  1. Standarddateien cgroups.json und task_profiles.json.
  2. API-Level-spezifische Dateien, falls vorhanden.
  3. Anbieterpartitionsdateien, falls vorhanden.

Änderungen an der vorhandenen API

Unter Android 10 und höher bleiben die Funktionen set_cpuset_policy, set_sched_policy und get_sched_policy ohne Änderungen an der API erhalten. In Android 10 werden diese Funktionen jedoch in libprocessgroup verschoben, das jetzt alle cgroup-bezogenen Funktionen enthält.

Der cutils/sched_policy.h-Header ist zwar weiterhin vorhanden, aber um zu vermeiden, dass vorhandener Code nicht mehr funktioniert, sollte neuer Code stattdessen einen neuen processgroup/sched_policy.h-Header enthalten.

Für Module, in denen eine dieser Funktionen verwendet wird, muss im zugehörigen Makefile eine Abhängigkeit von der libprocessgroup-Bibliothek hinzugefügt werden. Wenn ein Modul keine andere libcutils-Funktionalität verwendet, entfernen Sie die libcutils-Bibliotheksabhängigkeit aus dem Makefile.

APIs für Aufgabenprofile

Die privaten APIs in processgroup/processgroup.h sind in der Tabelle definiert:

Eingeben API und Definition
bool SetTaskProfiles(int tid, const std::vector& profiles)
Wendet die in profiles angegebenen Aufgabenprofile auf den Thread an, der durch eine Thread-ID (tid) mit dem Parameter tid angegeben wird.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Wendet die in profiles angegebenen Aufgabenprofile auf den Prozess an, der durch seine Nutzer- und Prozess-IDs mithilfe der Parameter uid und pid angegeben wird.
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Gibt zurück, ob ein von cgroup_name angegebener Cgroup-Controller vorhanden ist. Wenn true, wird die Variable path auf den Stamm dieser Cgroup gesetzt.
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
Gibt zurück, ob ein durch attr_name angegebenes Profilattribut vorhanden ist. Wenn true, wird die Variable path auf den Pfad der Datei gesetzt, die mit diesem Profilattribut verknüpft ist.
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
Gibt zurück, ob ein durch attr_name angegebenes Profilattribut vorhanden ist. Wenn true, wird die Variable path auf den Pfad der Datei gesetzt, die mit diesem Profilattribut verknüpft ist, und auf den Thread, der durch die Thread-ID mit dem Parameter tid angegeben wird.
bool UsePerAppMemcg()
Gibt zurück, ob das System für die Verwendung von Memory-Cgroups pro App konfiguriert ist.