Gerenciando acesso a recursos no Linux com control groups

- por Sergio Prado

Categorias: Linux Tags: ,

Control groups ou cgroups é uma implementação interessante do kernel Linux que permite particionar os recursos do sistema (CPU, memória, I/O) por grupo de processos.

Por exemplo, em um sistema Linux que roda um servidor web e um banco de dados você pode usar esta funcionalidade para alocar 50% de CPU para o grupo de processos responsáveis pelo acesso ao banco de dados, evitando que muitos acessos ao servidor web monopolizem a CPU.

Você consegue também alocar recursos por usuários em um servidor Linux. Por exemplo, usuários comuns tem acesso apenas à 30% da CPU e 1G de RAM, enquanto que administradores tem acesso à 70% da CPU e o restante da memória do sistema.

Para não ficar apenas em exemplos de servidores Linux que usam cgroups, o Android também é um usuário desta funcionalidade. Processos importantes no Android, incluindo a aplicação (activity) em foreground e os serviços do framework, são colocados em um grupo do cgroups para garantir acesso aos recursos de CPU e RAM quando necessário.

A documentação do cgroups está disponível no código-fonte do kernel em Documentation/cgroup-v1/cgroups.txt.

TESTANDO O CGROUPS

Os testes a seguir foram realizados na Beaglebone Black, mas podem ser reproduzidos em qualquer sistema Linux.

Usar o cgroups é relativamente tranquilo. O primeiro passo é habilitar no kernel a opção CONFIG_CGROUPS.

Com a opção de cgroups habilitada no kernel, você terá acesso à um sistema de arquivos virtual chamado cgroup, que será usado para particionar os recursos entre os processos do sistema.

# cat /proc/filesystems | grep cgroup
nodev cgroup

Este sistema de arquivos virtual é normalmente montado em /sys/fs/cgroup:

# mount -t cgroup cgroup /sys/fs/cgroup
# ls /sys/fs/cgroup
blkio.reset_stats                memory.max_usage_in_bytes
cgroup.clone_children            memory.move_charge_at_immigrate
cgroup.event_control             memory.oom_control
cgroup.procs                     memory.soft_limit_in_bytes
cpu.shares                       memory.stat
cpuacct.stat                     memory.swappiness
cpuacct.usage                    memory.usage_in_bytes
cpuacct.usage_percpu             memory.use_hierarchy
memory.failcnt                   notify_on_release
memory.force_empty               release_agent
memory.limit_in_bytes            tasks

Para testar o cgroups, criei um script para monopolizar a CPU:

# cat ~/burn.sh
#!/bin/sh
while true; do echo teste > /dev/null; done;

E executei o script 4 vezes:

# ~/burn.sh &
# ~/burn.sh &
# ~/burn.sh &
# ~/burn.sh &

Como esperado, o comando top me mostrou que os 4 scripts estão compartilhando o uso da CPU, cada um usando em torno de 25%.

# top
Mem: 12220K used, 498348K free, 0K shrd, 0K buff, 3772K cached
CPU:  45% usr  54% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 1.28 0.30 0.10 5/52 127
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  124   118 root     R     1904   0%  25% {burn.sh} /bin/sh /root/burn.sh
  126   118 root     R     1904   0%  25% {burn.sh} /bin/sh /root/burn.sh
  123   118 root     R     1904   0%  24% {burn.sh} /bin/sh /root/burn.sh
  125   118 root     R     1904   0%  24% {burn.sh} /bin/sh /root/burn.sh
   10     2 root     SW       0   0%   1% [rcu_sched]
  127   118 root     R     1976   0%   0% top
  114     1 root     S     3960   1%   0% /usr/sbin/sshd
  118     1 root     S     1976   0%   0% -sh
    1     0 root     S     1904   0%   0% init
   94     1 root     S     1904   0%   0% /sbin/syslogd -n
   97     1 root     S     1904   0%   0% /sbin/klogd -n
   21     2 root     SW       0   0%   0% [kworker/u:1]
   15     2 root     SW       0   0%   0% [kworker/0:1]
    2     0 root     SW       0   0%   0% [kthreadd]
    3     2 root     SW       0   0%   0% [ksoftirqd/0]
    4     2 root     SW       0   0%   0% [kworker/0:0]
    5     2 root     SW<      0   0%   0% [kworker/0:0H]
    6     2 root     SW       0   0%   0% [kworker/u:0]
    7     2 root     SW<      0   0%   0% [kworker/u:0H]
    8     2 root     SW       0   0%   0% [migration/0]

Vamos supor agora que eu queira dar uma fatia maior da CPU (80%) para o processo com o PID 124, e deixar os outros com o restante (20%).

Primeiro devo criar dois grupos no cgroups:

# mkdir /sys/fs/cgroup/group1
# mkdir /sys/fs/cgroup/group2

E definir as porcentagens de CPU para cada um deles:

# echo 10240 > /sys/fs/cgroup/group1/cpu.shares
# echo 2048 > /sys/fs/cgroup/group2/cpu.shares

Veja no comando acima que o grupo 2 ficou com 20% da CPU em relação ao grupo 1.

Por último, eu coloco cada um dos processos dentro do seu respectivo grupo:

# echo 124 > /sys/fs/cgroup/group1/tasks
# echo 126 > /sys/fs/cgroup/group2/tasks
# echo 123 > /sys/fs/cgroup/group2/tasks 
# echo 125 > /sys/fs/cgroup/group2/tasks

Agora, o comando top deverá me mostrar que o processo com PID 124 está consumindo em torno de 80% da CPU:

# top
Mem: 12236K used, 498332K free, 0K shrd, 0K buff, 3776K cached
CPU:  35% usr  64% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 4.03 4.00 3.71 5/49 136
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  124   118 root     R     1904   0%  82% {burn.sh} /bin/sh /root/burn.sh
  123   118 root     R     1904   0%   6% {burn.sh} /bin/sh /root/burn.sh
  126   118 root     R     1904   0%   6% {burn.sh} /bin/sh /root/burn.sh
  125   118 root     R     1904   0%   5% {burn.sh} /bin/sh /root/burn.sh
   10     2 root     SW       0   0%   1% [rcu_sched]
  136   118 root     R     1976   0%   0% top
  132     2 root     SW       0   0%   0% [kworker/0:0]
  114     1 root     S     3960   1%   0% /usr/sbin/sshd
  118     1 root     S     1976   0%   0% -sh
    1     0 root     S     1904   0%   0% init
   94     1 root     S     1904   0%   0% /sbin/syslogd -n
   97     1 root     S     1904   0%   0% /sbin/klogd -n
   21     2 root     SW       0   0%   0% [kworker/u:1]
   11     2 root     SW       0   0%   0% [watchdog/0]
   20     2 root     SW       0   0%   0% [irq/86-44e0b000]
    2     0 root     SW       0   0%   0% [kthreadd]
    3     2 root     SW       0   0%   0% [ksoftirqd/0]
    5     2 root     SW<      0   0%   0% [kworker/0:0H]
    7     2 root     SW<      0   0%   0% [kworker/u:0H]
    8     2 root     SW       0   0%   0% [migration/0]

Fácil, não?

OUTROS USOS

Control groups são bastante usados em servidores Linux. Existem diversas ferramentas em espaço de usuário como cgcreate, cgset e cgclassify, disponíveis no pacote libcgroup que facilitam o uso de cgroups. Um uso comum é na virtualização do sistema operacional através de containers. A implementação de LinuX Containers (LXC) faz exatamente isso usando cgroups.

E não existe nada que impeça que esta funcionalidade seja usada em sistemas embarcados, como o exemplo citado do Android. Principalmente quando os recursos de CPU e memória são escassos. Quanto mais ferramentas para controlar o acesso aos recursos do sistema, melhor.

Um abraço,

Sergio Prado

Faça um Comentário

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.