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

Sobre os cgroups

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

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 alterados, 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 de alterar 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 e os locais e atributos 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 deve ter pelo menos os seguintes itens:

  • 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 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 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 de inicialização inicial, 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 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 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 pelo próprio 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 abstraído 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 inclua a si mesmo).

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, sem afetar as camadas superiores. No exemplo abaixo, esses dois comandos executam efetivamente 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 do app superior sob o 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, que representam 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 mudar ou substituir as definições nos arquivos padrão cgroups.json e task_profiles.json, baseando a mudança no nível da API do Android ou usando a 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 para cgroups específicos de um nível da 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 de nível da API, se houver.
  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 a dependência da biblioteca libprocessgroup ao 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 particulares em processgroup/processgroup.h estã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 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.