Gerenciando acesso a recursos no Linux com control groups
- por Sergio Prado
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