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 adequados, em que as restrições são aplicadas, e as mudanças no conjunto de recursos do cgroup subjacente podem ser feitas sem afetar as camadas de software mais altas.

Sobre cgroups

Os cgroups fornecem um mecanismo para agregar e particionar conjuntos de tarefas (que consistem em processos, linhas de execução e todos os filhos futuros deles) 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 eles pudessem ser mudados, o framework do Android esperava que um conjunto específico de cgroups existisse em locais específicos com uma versão e uma hierarquia de subgrupos específicas, com base no script. Isso limitava a capacidade de escolher a próxima versão do cgroup a ser usada ou de 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 do cgroup. Os desenvolvedores descrevem a configuração dos cgroups no arquivo cgroups.json para definir conjuntos de cgroups, além dos locais e atributos de montagem. Todos os cgroups são montados durante o estágio de inicialização antecipada 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 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 legadas set_cpuset_policy, set_sched_policy e get_sched_policy fornecem 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 as novas APIs de perfis de tarefas em vez da função legada set_sched_policy.

Arquivo de descrição dos cgroups

Os 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 precisa ter, no mínimo, o seguinte:

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

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 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 cgroup especificado com caminho e modo como "Optional" ao definir como true.

O arquivo cgroups.json é analisado como parte do processo de inicialização, durante a fase de inicialização antecipada, e os cgroups são montados nos locais especificados. Para obter 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 uma thread. 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 Atributos. Cada entrada contém o seguinte:

  • O campo Nome especifica o nome do atributo.
  • O campo Controlador faz referência a um controlador de cgroup do arquivo cgroups.json pelo nome.
  • O campo Arquivo nomeia um arquivo específico nesse controlador.

Os atributos são referências nas definições de perfil de tarefa. Fora dos perfis de tarefa, use-os somente quando o framework exigir acesso direto a esses arquivos, e o acesso não puder ser abstraído usando perfis de tarefa. Em todos os outros casos, use perfis de tarefas. Eles oferecem um desacoplamento melhor entre o comportamento necessário e os detalhes da 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 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 Timer slack 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 cgroup de cgroups.json
Path Um caminho de subgrupo na hierarquia do controlador cgroup

O Android 12 e versões mais recentes têm uma seção AggregateProfiles que contém perfis agregados, cada um deles sendo um alias para um conjunto de um ou mais perfis. As definições de perfil agregadas 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 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 task_profiles init language

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 tarefa para um processo específico. Ele substitui o comando writepid (descontinuado no Android 12) 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. Era usado para gravar o PID da tarefa atual no arquivo /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    Adiciona 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.

Perfis de tarefas por nível de API

No Android 12 e versões mais recentes, é possível alterar ou substituir definições nos arquivos cgroups.json e task_profiles.json padrão, seja baseando a mudança no nível da API do Android ou fazendo isso 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 cgroups 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 de 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 do 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 padrão cgroups.json e task_profiles.json.
  2. Arquivos específicos do nível da API, se houver.
  3. Arquivos de partição do fornecedor, se houver.

Mudanças na API atual

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, remova a dependência da biblioteca libcutils do makefile.

APIs de perfis de tarefas

As APIs particulares 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) usando o parâmetro tid.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Aplica os perfis de tarefa especificados em profiles ao processo especificado pelos IDs de usuário e de 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 dela usando o parâmetro tid.
bool UsePerAppMemcg()
Retorna se o sistema está configurado para usar cgroups de memória por app.