Camada de abstração do Cgroup

O Android 10 e versões posteriores usam uma camada de abstração de grupo de controle (cgroup) com perfis de tarefas, que os desenvolvedores podem usar para descrever um conjunto (ou conjuntos) de restrições a serem aplicadas a um thread ou processo. O sistema então segue as ações prescritas dos perfis de tarefa para selecionar um ou mais cgroups apropriados, através dos quais as restrições são aplicadas, e alterações no conjunto de recursos do cgroup subjacente podem ser feitas sem afetar as camadas superiores de software.

Sobre cgroups

Cgroups fornecem um mecanismo para agregar e particionar conjuntos de tarefas (que consistem em processos, threads e todos os seus futuros filhos) em grupos hierárquicos com comportamento especializado. O Android usa cgroups para controlar e contabilizar recursos do sistema, como CPU e uso e alocação de memória, com suporte para cgroups v1 e cgroups v2 do kernel Linux.

Android 9 e inferior

No Android 9 e versões anteriores, o script de inicialização init.rc continha o conjunto de cgroups disponíveis, seus pontos de montagem e versões. Embora estes pudessem ser alterados, a estrutura do Android esperava que existisse um conjunto específico de cgroups em locais específicos com uma versão específica e hierarquia de subgrupos, com base no script. Isso limitou a capacidade de escolher a próxima versão do cgroup a ser usada ou de alterar a hierarquia do cgroup para usar novos recursos.

Android 10 e superior

Android 10 e superior usam cgroups com perfis de tarefas:

  • Configuração do grupo C. Os desenvolvedores descrevem a configuração dos cgroups em seu arquivo cgroups.json para definir conjuntos de cgroups e seus locais de montagem e atributos. Todos os cgroups são montados durante o estágio inicial do processo de inicialização.
  • Perfis de tarefas. Eles fornecem uma abstração que separa a funcionalidade necessária dos detalhes de sua implementação. A estrutura do Android aplica os perfis de tarefas conforme descrito no arquivo task_profiles.json a um processo ou thread usando as APIs SetTaskProfiles e SetProcessProfiles . (Essas APIs são exclusivas do Android 11 e versões posteriores.)

Para fornecer compatibilidade com versões anteriores, as funções herdadas set_cpuset_policy , set_sched_policy e get_sched_policy fornecem a mesma API e funcionalidade, mas sua implementação foi modificada para usar perfis de tarefas. Para novos casos de uso, o AOSP recomenda o uso de novas APIs de perfis de tarefas em vez da função herdada set_sched_policy .

Arquivo de descrição do Cgroups

Cgroups são descritos no arquivo cgroups.json localizado em <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ . Cada controlador é descrito em uma subseção e deve ter no mínimo o seguinte:

  • Nome, definido pelo campo Controlador .
  • Caminho de montagem, definido pelo campo Caminho .
  • Mode , UID (ID do usuário) e GID (ID do grupo) descrevendo o proprietário e os modos de acesso dos arquivos neste caminho (todos opcionais).
  • Atributo opcional , definido como true para permitir que o sistema ignore o erro de montagem causado por um controlador cgroup cujo kernel não suporta montagem.

Exemplo de arquivo cgroups.json

O exemplo abaixo mostra descrições para controladores cgroup v1 ( Cgroups ) e cgroup v2 ( Cgroups2 ) com seus respectivos caminhos.

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

Este arquivo de exemplo contém duas seções, Cgroups (descrevendo os controladores cgroup v1) e Cgroups2 (descrevendo os controladores cgroup v2). Todos os controladores na hierarquia cgroups v2 são montados no mesmo local. Portanto, a seção Cgroups2 possui seus próprios atributos Path , Mode , UID e GID para descrever a localização e os atributos da raiz da hierarquia. O atributo Path para Controladores em Cgroups2 é relativo a esse caminho raiz. No Android 12 e superior, você pode definir um controlador cgroup especificado com caminho e modo como "Optional" definindo-o como true .

O arquivo cgroups.json é analisado como parte do processo init, durante o estágio inicial, e os cgroups são montados nos locais especificados. Para obter posteriormente os locais de montagem do cgroup, use a função da API CgroupGetControllerPath .

Arquivo de perfis de tarefas

O arquivo task_profiles.json está localizado em <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/ . Use-o para descrever um conjunto específico de ações a serem aplicadas a um processo ou thread. Um conjunto de ações está associado a um nome de perfil, que é usado em chamadas SetTaskProfiles e SetProcessProfiles para invocar ações de perfil.

Exemplo de arquivo 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" ]
     }
}

Atribua nomes a arquivos cgroup específicos como entradas em sua lista de atributos . Cada entrada contém o seguinte:

  • O campo Nome especifica o nome do Atributo.
  • O campo Controller faz referência a um controlador cgroup do arquivo cgroups.json , por seu nome.
  • O campo Arquivo nomeia um arquivo específico neste controlador.

Atributos são referências em definições de perfil de tarefa. Fora dos perfis de tarefas, use-os somente quando a estrutura exigir acesso direto a esses arquivos e o acesso não puder ser abstraído usando perfis de tarefas. Em todos os outros casos, utilize perfis de tarefa; eles fornecem melhor dissociação entre o comportamento necessário e os detalhes de sua implementação.

A seção Perfis contém definições de perfil de tarefa com o seguinte:

  • O campo Nome define o nome do perfil.
  • A seção Ações lista um conjunto de ações executadas quando o perfil é aplicado. Cada ação tem o seguinte:

    • O campo Nome especifica a ação.
    • A seção Params especifica um conjunto de parâmetros para a ação.

As ações suportadas estão listadas na tabela:

Ação Parâmetro Descrição
SetTimerSlack Slack Folga do temporizador em ns
SetAttribute Name Um nome que faz referência a um atributo da seção Atributos
Value Um valor a ser gravado no arquivo representado pelo atributo nomeado
WriteFile FilePath caminho para o arquivo
Value um valor a ser gravado no arquivo
JoinCgroup Controller Um nome do controlador cgroup de cgroups.json
Path Um caminho de subgrupo na hierarquia do controlador cgroup

O Android 12 e versões posteriores apresentam uma seção AggregateProfiles que contém perfis agregados, cada um dos quais é um alias para um conjunto de um ou mais perfis. As definições de perfil agregado consistem no seguinte:

  • O campo Nome especifica o nome do perfil agregado.
  • O campo Perfis lista os nomes dos perfis incluídos no perfil agregado.

Quando um perfil agregado é aplicado, todos os perfis contidos também são aplicados automaticamente. Os perfis agregados podem conter perfis individuais ou outros perfis agregados, desde que não haja recursões (um perfil que se inclui).

comando de linguagem de inicialização task_profiles

Um comando task_profiles na linguagem Android Init está disponível para Android 12 e versões posteriores para facilitar a ativação do perfil de tarefa para um processo específico. Ele substitui o comando writepid (obsoleto no Android 12) que foi usado para migrar um processo entre cgroups. O comando task_profiles fornece flexibilidade para alterar implementações subjacentes sem efeito nas camadas superiores. No exemplo abaixo, esses dois comandos executam efetivamente a mesma operação:

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

    Obsoleto no Android 12, foi usado para gravar o PID da tarefa atual no arquivo /dev/cpuctl/top-app/tasks .

  • task_profiles MaxPerformance

    Ingressa o processo atual no grupo top-app no ​​controlador "cpu" ( cpuctl ), o que resulta na gravação do PID do processo em dev/cpuctl/top-app/tasks .

Sempre use o comando task_profiles para migrar tarefas em hierarquias cgroup no Android 12 e versões posteriores. Aceita um ou mais parâmetros, representando os nomes dos perfis especificados no arquivo task_profiles.json .

Perfis de tarefa por nível de API

No Android 12 e versões posteriores, você pode alterar ou substituir definições nos arquivos padrão cgroups.json e task_profiles.json , baseando sua alteração no nível da API do Android ou fazendo-a na partição do fornecedor.

Para substituir as definições com base no nível da API, os seguintes arquivos devem estar presentes no dispositivo:

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

    Use isto para cgroups específicos para um nível de API.

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

    Use isto para perfis específicos de um nível de API.

Para substituir as definições da partição do fornecedor, os seguintes arquivos devem estar presentes no dispositivo:

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

Se um atributo ou uma definição de perfil nesses arquivos usar o mesmo nome do arquivo padrão, a definição do arquivo (nível de API ou nível de fornecedor) substituirá a definição anterior. Observe também que as definições em nível de fornecedor substituem as definições em nível de API. Se a nova definição tiver um novo nome, o conjunto de atributos ou perfis será alterado com a nova definição.

O sistema Android carrega os arquivos cgroup e task_profile nesta ordem:

  1. Arquivos padrão cgroups.json e task_profiles.json .
  2. Arquivos específicos do nível da API, se presentes.
  3. Arquivos de partição do fornecedor, se houver.

Mudanças na API existente

O Android 10 e versões posteriores mantêm as funções set_cpuset_policy , set_sched_policy e get_sched_policy sem alterações na API. No entanto, o Android 10 move essas funções para libprocessgroup , que agora contém todas as funcionalidades relacionadas ao cgroup.

Embora o cabeçalho cutils/sched_policy.h ainda exista, para evitar quebrar o código existente, certifique-se de que o novo código inclua um novo cabeçalho processgroup/sched_policy.h .

Módulos que usam qualquer uma dessas funções devem adicionar dependência da biblioteca libprocessgroup em seu makefile. Se um módulo não usar nenhuma outra funcionalidade libcutils , elimine a dependência da biblioteca libcutils do makefile.

APIs de perfis de tarefas

As APIs privadas em processgroup/processgroup.h são definidas na tabela:

Tipo API e definição
bool SetTaskProfiles(int tid, const std::vector & profiles) SetTaskProfiles(int tid, const std::vector & profiles)
Aplica os perfis de tarefa especificados em profiles ao thread especificado por um ID de thread (tid) usando seu parâmetro tid .
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles)
Aplica os perfis de tarefa especificados em profiles ao processo especificado por seu usuário e IDs de processo usando parâmetros uid e pid
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Retorna se existe um controlador cgroup especificado por cgroup_name ; se true , define a variável path para a raiz desse cgroup
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
Retorna se existe um atributo de perfil especificado por attr_name ; se true , definirá a variável path como o caminho do arquivo associado a esse atributo de perfil.
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
Retorna se existe um atributo de perfil especificado por attr_name ; se true , definirá a variável path para o caminho do arquivo associado a esse atributo de perfil e para o encadeamento especificado por seu ID de encadeamento usando o parâmetro tid .
bool UsePerAppMemcg()
Retorna se o sistema está configurado para usar cgroups de memória por aplicativo.