cgroup 抽象化レイヤ

Android 10 以降では、タスク プロファイルによるコントロール グループ(cgroup)抽象化レイヤを使用します。これによりデベロッパーは、スレッドまたはプロセスに適用する制限のセットを記述できます。システムはその後、タスク プロファイルの所定のアクションに従って、制限を適用する 1 つ以上の適切な cgroup を選択します。また、上位のソフトウェア レイヤに影響を与えることなく、基となる cgroup 機能セットを変更できます。

cgroup について

cgroup は、タスクのセット(プロセス、スレッド、その将来のすべての子から構成)を、特定の動作の階層グループに集約し分割するメカニズムを提供します。Android では cgroups を使用して、CPU とメモリの使用量や割り当てなどのシステム リソースを制御、考慮しており、Linux カーネルの cgroups v1cgroups v2 をサポートしています。

Android 9 以前

Android 9 以前では、init.rc 初期化スクリプトに、利用可能な cgroup のセット、マウント ポイント、バージョンが含まれていました。これらは変更できますが、Android フレームワークでは、スクリプトに基づいて、特定の cgroup セットが特定の場所に存在し、固有のバージョンとサブグループ階層を持つことが想定されていました。このため、次に使用する cgroup バージョンを選択する機能や、cgroup の階層を変更して新しい機能を使用する機能が制限されていました。

Android 10 以降

Android 10 以降では、タスク プロファイルで cgroup を使用します。

  • cgroup の設定 - デベロッパーは cgroups.json ファイルに cgroup の設定を記述して、cgroup のセット、マウント場所と属性を定義します。cgroup はすべて、初期化プロセスの初期段階でマウントされます。
  • タスク プロファイル - 必要な機能を実装の詳細から切り離す抽象化を行います。Android フレームワークは、task_profiles.json ファイルに記載されているタスク プロファイルを、SetTaskProfiles API と SetProcessProfiles API を使用してプロセスまたはスレッドに適用します(これらの API は Android 11 以降に固有のものです)。

下位互換性を確保するために、以前の関数 set_cpuset_policyset_sched_policyget_sched_policy で同じ API と機能が提供されますが、タスク プロファイルを使用するように実装が変更されています。新しいユースケースでは、AOSP は以前の set_sched_policy 関数の代わりに新しいタスク プロファイル API を使用することを推奨しています。

cgroup 記述ファイル

cgroup は、<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ にある cgroups.json ファイルに記述します。各コントローラはサブセクションで記述します。少なくとも次のものが必要です。

  • 名前。Controller フィールドで定義します。
  • マウントパス。Path フィールドで定義します。
  • ModeUID(ユーザー ID)、GID(グループ ID)。このパスにあるファイルの所有者とアクセスモードを記述します(すべて省略可能)。
  • Optional 属性。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 コントローラを記述)という 2 つのセクションがあります。cgroups v2 階層のコントローラはすべて、同じ場所にマウントされます。したがって、Cgroups2 セクションには、階層のルートの場所と属性を記述する固有の PathModeUID、および GID 属性があります。Cgroups2 にある ControllerPath 属性は、そのルートパスに対する相対パスです。Android 12 以降では、"Optional"true に設定することにより、パスとモードが省略可能として指定された cgroup コントローラを定義できます。

cgroups.json ファイルは初期の init ステージで init プロセスの一部として解析され、指定された場所に cgroup がマウントされます。cgroup のマウント場所を後で取得するには、CgroupGetControllerPath API 関数を使用します。

タスク プロファイルのファイル

task_profiles.json ファイルは <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ にあります。プロセスまたはスレッドに適用される一連のアクションを記述するために使用します。一連のアクションはプロファイル名に関連付けられます。プロファイル名はプロファイル アクションを実行するために SetTaskProfilesSetProcessProfiles の呼び出しで使用されます。

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

Attributes リストのエントリとして、特定の cgroup ファイルに名前を割り当てます。各エントリには次のものが含まれます。

  • [Name] フィールド - 属性の名前を指定します。
  • [Controller] フィールド - cgroups.json ファイルから cgroup コントローラを名前で参照します。
  • [File] フィールド - このコントローラにある特定のファイルを指定します。

Attributes は、タスク プロファイル定義内の参照です。タスク プロファイル以外では、フレームワークがこれらのファイルに直接アクセスする必要があり、タスク プロファイルを使用してアクセスを抽象化できない場合にのみ使用してください。それ以外の場合は、タスク プロファイルを使用します。必要な動作とその実装の詳細を適切に分離できます。

[Profiles] セクションには、次のようなタスク プロファイルの定義が含まれます。

  • [Name] フィールド - プロファイル名を定義します。
  • [Actions] セクション - プロファイルを適用したときに実行されるアクションのセットをリストします。各アクションの内容は次のとおりです。

    • [Name] フィールド: アクションを指定します。
    • [Params] セクション: アクションのパラメータのセットを指定します。

サポートされるアクションを下表に示します。

アクション パラメータ 説明
SetTimerSlack Slack タイマーのゆとり(ナノ秒)
SetAttribute Name [Attributes] セクションの属性を参照する名前
Value 名前付き属性で表される、ファイルに書き込まれる値
WriteFileFilePathファイルのパス
Valueファイルに書き込まれる値
JoinCgroup Controller cgroups.json の cgroup コントローラの名前
Path cgroup コントローラの階層内のサブグループ パス

Android 12 以降では、集計プロファイルを含む AggregateProfiles セクションが導入されています(各プロファイルは 1 つ以上のプロファイルのセットのエイリアスです)。集計プロファイルの定義は以下の要素で構成されます。

  • Name フィールド - 集計プロファイルの名前を指定します。
  • Profiles フィールド - 集計プロファイルに含まれるプロファイルの名前のリストです。

集計プロファイルが適用されると、含まれているすべてのプロファイルも自動的に適用されます。再帰プロファイル(自分自身を含むプロファイル)がなければ、個々のプロファイルまたは他の集計プロファイルの両方を集計プロファイルに含めることができます。

init 言語の task_profiles コマンド

Android 12 以降では、特定のプロセスのタスク プロファイルのアクティブ化を容易にするために、Android init 言語task_profiles コマンドを使用できます。これは、cgroup 間でプロセスを移行するために使用されていた writepid コマンド(Android 12 でサポート終了)に代わるものです。task_profiles コマンドを使用すると、上位レイヤに影響を与えることなく、基盤となる実装を柔軟に変更できます。以下の例に示す 2 つのコマンドは、結果的に同じオペレーションを実行します。

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

    現在のタスクの PID を /dev/cpuctl/top-app/tasks ファイルに書き込むために使用されていましたが、Android 12 でサポートが終了しました。

  • task_profiles MaxPerformance

    現在のプロセスを「cpu」コントローラ(cpuctl)の下の top-app グループに追加します。これにより、プロセスの PID が dev/cpuctl/top-app/tasks に書き込まれます。

Android 12 以降では、cgroup 階層のタスクを移行するには、常に task_profiles コマンドを使用します。このコマンドは、task_profiles.json ファイルで指定されたプロファイルの名前を表す 1 つ以上のパラメータを受け入れます。

API レベルのタスク プロファイル

Android 12 以降では、Android API レベルでの変更に基づいて、またはベンダー パーティションから、デフォルトの cgroups.json ファイルと task_profiles.json ファイルで定義を修正またはオーバーライドできます。

API レベルに基づいて定義をオーバーライドするには、以下のファイルがデバイスに存在する必要があります。

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

    これは、API レベル固有の cgroup に使用します。

  • /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_policyset_sched_policyget_sched_policy が維持され、API は変更されていません。ただし、Android 10 ではこれらの関数が libprocessgroup に移動され、ここに cgroup 関連のすべての機能が含まれるようになりました。

cutils/sched_policy.h ヘッダーはまだ存在しますが、既存のコードを破損しないように、新しいコードでは代わりに新しい processgroup/sched_policy.h ヘッダーを含めるようにしてください。

これらの関数を使用するモジュールでは、libprocessgroup ライブラリへの依存関係を makefile に追加する必要があります。モジュールが他の libcutils 機能を使用しない場合は、makefile から libcutils ライブラリ依存関係を削除してください。

タスク プロファイル API

processgroup/processgroup.h 内の非公開 API の定義を以下の表に示します。

API と定義
bool SetTaskProfiles(int tid, const std::vector& profiles)

profiles で指定されたタスク プロファイルを、tid パラメータを使用してスレッド ID(tid)で指定されたスレッドに適用します。

bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)

profiles で指定されたタスク プロファイルを、uid パラメータと pid パラメータを使用してユーザー ID とプロセス ID で指定されたプロセスに適用します。

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

cgroup_name で指定された cgroup コントローラが存在するかどうかを返します。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 パラメータを使用してスレッド ID で指定されたスレッドに設定します。

bool UsePerAppMemcg()

アプリごとのメモリ cgroup を使用するようにシステムが構成されているかどうかを返します。