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
