Camada de abstração Cgroup

O Android 10 e versões mais recentes 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 uma linha de execução ou a um processo. Em seguida, o sistema segue as ações prescritas dos perfis de tarefas para selecionar um ou mais cgroups apropriados, em que as restrições são aplicadas, e as mudanças no conjunto de recursos de cgroup podem ser feitas sem afetar camadas de software mais altas.

Sobre os cgroups

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

Android 9 e versões anteriores

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

Android 10 e versões mais recentes

O Android 10 e versões mais recentes usam cgroups com perfis de tarefas:

  • Configuração de grupos. Os desenvolvedores descrevem a configuração de cgroups no arquivo cgroups.json para definir conjuntos de cgroups e os atributos e locais de montagem deles. Todos os cgroups são montados durante a fase inicial do processo de inicialização.
  • Perfis de tarefas. Elas fornecem uma abstração que separa a funcionalidade necessária dos detalhes da implementação. O framework do Android aplica os perfis de tarefas, conforme descrito no arquivo task_profiles.json, a um processo ou linha de execução usando as APIs SetTaskProfiles e SetProcessProfiles. Essas APIs são exclusivas do Android 11 e versões mais recentes.

Para oferecer compatibilidade com versões anteriores, as funções set_cpuset_policy, set_sched_policy e get_sched_policy legadas oferecem a mesma API e funcionalidade, mas a implementação delas foi modificada para usar perfis de tarefas. Para novos casos de uso, o AOSP recomenda usar novas APIs de perfil de tarefas em vez da função set_sched_policy legada.

Arquivo de descrição de cgroups

Os grupos C são descritos no arquivo cgroups.json localizado em <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Cada controlador é descrito em uma subseção e precisa 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) que descrevem o proprietário e os modos de acesso para os arquivos nesse caminho (todos opcionais).
  • Atributo Opcional, definido como true para permitir que o sistema ignore o erro de montagem causado por um controlador de cgroup que o kernel não oferece suporte para montagem.

Exemplo de arquivo cgroups.json

O exemplo abaixo mostra descrições para controladores cgroup v1 (Cgroups) e cgroup v2 (Cgroups2) com os 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 (que descreve os controladores cgroup v1) e Cgroups2 (que descreve os controladores cgroup v2). Todos os controladores na hierarquia de cgroups v2 são montados no mesmo local. Portanto, a seção Cgroups2 tem 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 Controllers em Cgroups2 é relativo a esse caminho raiz. No Android 12 e versões mais recentes, é possível definir um controlador de cgroup especificado com caminho e modo como "Optional" definindo-o como true.

O arquivo cgroups.json é analisado como parte do processo de inicialização, durante a fase inicial de inicialização, e os cgroups são montados nos locais especificados. Para acessar os locais de montagem do cgroup mais tarde, 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 a uma linha de execução. Um conjunto de ações é 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 na 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 de cgroup do arquivo cgroups.json pelo nome.
  • O campo File nomeia um arquivo específico neste controlador.

Os atributos são referências nas definições de perfil de tarefas. Fora dos perfis de tarefas, use-os somente quando o framework exigir acesso direto a esses arquivos e o acesso não puder ser abstrato usando perfis de tarefas. Em todos os outros casos, use perfis de tarefas. Eles oferecem um melhor desacoplamento entre o comportamento necessário e os detalhes de implementação.

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

  • O campo Name define o nome do perfil.
  • A seção Ações lista um conjunto de ações realizadas 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 compatíveis estão listadas na tabela:

Ação Parâmetro Descrição
SetTimerSlack Slack Tempo de espera do timer 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
WriteFileFilePathcaminho para o arquivo
Valueum valor a ser gravado no arquivo
JoinCgroup Controller Um nome do controlador de cgroup de cgroups.json
Path Um caminho de subgrupo na hierarquia do controlador de cgroup

O Android 12 e versões mais recentes têm uma seção AggregateProfiles que contém perfis agregados, cada um deles é um alias para um conjunto de um ou mais perfis. As definições de perfil agregado consistem em:

  • 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 que o contêm 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 task_profiles init

Um comando task_profiles na Linguagem de inicialização do Android está disponível para o Android 12 e versões mais recentes para facilitar a ativação do perfil de tarefas para um processo específico. Ele substitui o comando writepid (descontinuado no Android 12) que era usado para migrar um processo entre cgroups. O comando task_profiles oferece flexibilidade para mudar as implementações subjacentes sem afetar as camadas superiores. No exemplo abaixo, esses dois comandos executam a mesma operação:

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

    Descontinuado no Android 12, esse recurso era usado para gravar o PID da tarefa atual no arquivo /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    Une o processo atual ao grupo de apps principais 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 de cgroup no Android 12 e versões mais recentes. Ele aceita um ou mais parâmetros, representando os nomes dos perfis especificados no arquivo task_profiles.json.

Por perfis de tarefas no nível da API

No Android 12 e versões mais recentes, é possível modificar ou substituir definições nos arquivos cgroups.json e task_profiles.json padrão, baseando a mudança no nível da API do Android ou na partição do fornecedor.

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

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

    Use isso para grupos de controle específicos de um nível de API.

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

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

Para substituir as definições da partição do fornecedor, os seguintes arquivos precisam 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 que está no arquivo padrão, a definição do arquivo (no nível da API ou do fornecedor) vai substituir a definição anterior. As definições no nível do fornecedor substituem as definições no nível da 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 cgroups.json e task_profiles.json padrão.
  2. Arquivos específicos do nível da API, se presentes.
  3. Arquivos de partição do fornecedor, se presentes.

Mudanças na API

O Android 10 e versões mais recentes mantêm as funções set_cpuset_policy, set_sched_policy e get_sched_policy sem mudanças 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 a quebra do código atual, verifique se o novo código inclui um novo cabeçalho processgroup/sched_policy.h.

Os módulos que usam qualquer uma dessas funções precisam adicionar dependência à biblioteca libprocessgroup no makefile. Se um módulo não usar nenhuma outra funcionalidade libcutils, exclua 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)
Aplica os perfis de tarefa especificados em profiles à linha de execução especificada por um ID de linha de execução (tid, na sigla em inglês) usando o parâmetro tid.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Aplica os perfis de tarefas especificados em profiles ao processo especificado pelos IDs de usuário e processo usando os parâmetros uid e pid
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Retorna se um controlador de cgroup especificado por cgroup_name existe. Se true, define a variável path como a raiz desse cgroup
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
Retorna se um atributo de perfil especificado por attr_name existe. Se true, define 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 um atributo de perfil especificado por attr_name existe. Se true, define a variável path como o caminho do arquivo associado a esse atributo de perfil e à linha de execução especificada pelo ID da linha de execução usando o parâmetro tid.
bool UsePerAppMemcg()
Retorna se o sistema está configurado para usar cgroups de memória por app.