Dedicando uma CPU para processos específicos

Publicado por Marcelo Moreira de Mello em 04/11/2009

[ Hits: 9.680 ]

Blog: http://tchellomello.blogspot.com

 


Dedicando uma CPU para processos específicos



Já desejou enviar algum processo para uma CPU dedicada? Se sim mas não sabe como, essa dica irá lhe ajudar a desvendar este mistério.

A primeira configuração que precisamos fazer é, em tempo de boot, passar o parâmetro "isolcpus=" para isolarmos uma ou várias CPUs do scheduler padrão, para assim enviarmos processos para esses CPUs isolados.

No laptop que estou utilizando, tenho 1 processador Dual Core, portanto o kernel reconhecerá os processadores com id 0 e 1. Como precisamos de pelo menos 1 processador para bootar a máquina, irei escolher o processador com o ID1 para ficar isolado do scheduler.

# cat /etc/grub.conf

# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/mmello_vg0/lv.root
# initrd /initrd-version.img
#boot=/dev/sda

default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
password --md5 $1$trwxdaUZ$Ae.coXq5yTQQsLduydi6z0
title Red Hat Enterprise Linux Server (2.6.18-164.2.1.el5)
   root (hd0,0)
   kernel /vmlinuz-2.6.18-164.2.1.el5 ro root=/dev/mmello_vg0/lv.root rhgb quiet isolcpus=1
   initrd /initrd-2.6.18-164.2.1.el5.img

# cat /proc/cmdline
ro root=/dev/mmello_vg0/lv.root rhgb quiet isolcpus=1

Configurado o Grub e inicializada a máquina, conforme evidências acima, podemos listar todos os processos da máquina. Os processos estarão atribuídos somente ao processador incluso no scheduler, no caso o CPU 0.

# ps axo pid,comm,psr

  PID COMMAND         PSR
    1 init              0
    2 migration/0       0
    3 ksoftirqd/0       0
    4 watchdog/0        0
    5 migration/1       1
    6 ksoftirqd/1       1
    7 watchdog/1        1
    8 events/0          0
    9 events/1          1
   10 khelper           0
   11 kthread           0
   15 kblockd/0         0
   16 kblockd/1         1
   17 kacpid            0
  135 cqueue/0          0
  136 cqueue/1          1
  139 khubd             0
  141 kseriod           0
  208 pdflush           0
  209 pdflush           0
  210 kswapd0           0
  211 aio/0             0
  212 aio/1             1
  367 pccardd           0
  377 kpsmoused         0
  409 ata/0             0
  410 ata/1             1
  411 ata_aux           0
  415 scsi_eh_0         0
  416 scsi_eh_1         0
  417 scsi_eh_2         0
  418 scsi_eh_3         0
  425 kstriped          0
  438 ksnapd            0
  453 kjournald         0
  481 kauditd           0
  514 udevd             0
 1178 iwl3945/0         0
 1179 iwl3945/1         1
 1181 iwl3945           0
 1508 hd-audio0         0
 2061 kcryptd_io        0
 2062 kcryptd           0
 2071 kmpathd/0         0
 2072 kmpathd/1         1
 2073 kmpath_handlerd   0
 2162 kjournald         0
 2170 kjournald         0
 2175 kjournald         0
 2413 kondemand/0       0
 2414 kondemand/1       1
 2503 auditd            0
 2505 audispd           0
 2537 syslogd           0
 2540 klogd             0
 2576 portmap           0
 2606 rpciod/0          0
 2607 rpciod/1          1
 2614 rpc.idmapd        0
 2639 dbus-daemon       0
 2665 acpid             0
 2679 hald              0
 2680 hald-runner       0
 2687 hald-addon-acpi   0
 2694 hald-addon-keyb   0
 2703 hald-addon-keyb   0
 2706 hald-addon-keyb   0
 2709 hald-addon-keyb   0
 2715 hald-addon-stor   0
 2754 sshd              0
 2768 cupsd             0
 2769 cups-polld        0
 2784 xinetd            0
 2807 sendmail          0
 2815 sendmail          0
 2830 gpm               0
 2844 nasd              0
 2858 crond             0
 2890 xfs               0
 2917 atd               0
 2948 libvirtd          0
 2963 rhnsd             0
 3031 NetworkManager    0
 3054 wpa_supplicant    0
 3056 nm-system-setti   0
 3058 dnsmasq           0
 3072 smartd            0
 3098 mingetty          0
 3099 mingetty          0
 3102 mingetty          0
 3105 mingetty          0
 3120 mingetty          0
 3122 mingetty          0
 3123 gdm-binary        0
 3184 gdm-binary        0
 3186 gdm-rh-security   0
 3189 Xorg              0
 3237 yum-updatesd      0
 3239 gam_server        0
 3251 gnome-session     0
 3287 ssh-agent         0
 3316 dbus-launch       0
 3317 dbus-daemon       0
 3323 gconfd-2          0
 3326 gnome-keyring-d   0
 3328 gnome-settings-   0
 3348 metacity          0
 3352 gnome-panel       0
 3354 nautilus          0
 3355 gnome-volume-ma   0
 3357 bonobo-activati   0
 3360 pidgin            0
 3362 gnome-vfs-daemo   0
 3366 eggcups           0
 3376 bt-applet         0
 3380 nm-applet         0
 3385 puplet            0
 3400 escd              0
 3401 gnome-power-man   0
 3418 mapping-daemon    0
 3425 gweather-applet   0
 3427 wnck-applet       0
 3455 trashapplet       0
 3482 gam_server        0
 3501 stickynotes_app   0
 3504 mixer_applet2     0
 3506 clock-applet      0
 3511 pam-panel-icon    0
 3512 pam_timestamp_c   0
 3516 notification-ar   0
 3518 dhclient          0
 3551 gnome-screensav   0
 3566 gnome-terminal    0
 3568 gnome-pty-helpe   0
 3569 bash              0
 3595 su                0
 3598 bash              0
 3702 bash              0
 3741 run-mozilla.sh    0
 3766 firefox           0
 3803 npviewer.bin      0
 3822 bash              0
 3847 vim               0
 3849 su                0
 3852 bash              0
 3891 bash              0
 3918 su                0
 3921 bash              0
 3955 ps                0

Obs.: Por mais que se isole um ou vários processadores do scheduler, alguns processos a nível de kernel-space já estarão alocados aguardando por tarefas user-space.

Isolada a CPU1, podemos agora utilizar um mecanismo implementado pelo kernel que permite atribuir processos específicos para determinadas CPUs chamado de cpuset.

Para utilizar a facilidade do cpuset, necessitamos primeiramente montar um sistema de arquivos especial no sistema do tipo cpuset que será utilizado para atribuirmos as tarefas.

# mkdir /cpuset
# mount -t cpuset none /cpuset/
# mount| grep cpuset

none on /cpuset type cpuset (rw)
# ls /cpuset/
cpu_exclusive mem_exclusive
memory_pressure
memory_spread_page
mems
sched_relax_domain_level
cpus
memory_migrate
memory_pressure_enabled
memory_spread_slab
notify_on_release
tasks

Cada conjunto cpuset é representado por um diretório do tipo cpuset que contém alguns arquivos, dentre eles os principais:
  • cpus: Lista de CPUs no cpuset.
  • mems: Lista de Memory Nodes disponível no conjunto cpuset (basicamente a área de memória cache de cada processador). Em arquitetura NUMA, deve-se visualizar mais do que 1 área.
  • tasks: Lista dos processos (PID) atribuídos ao cpuset.

Como o laptop que estou usando não possui "NUMA", o conteúdo do arquivos mems exibirá somente 1 área de memória e o arquivo cpus exibirá os 2 processadores (cores) que possuo. Já o arquivo tasks irá reportar todos os processos das cpus 0 e 1.

# cat /cpuset/mems
0
# cat /cpuset/cpus
0-1
# wc -l /cpuset/tasks
170 /cpuset/tasks

Para facilitar o gerenciamento dos processos por parte do administrador, iremos criar 1 diretório chamado cpu1 dentro do sistema de arquivos cpuset, que automaticamente será populado com os arquivos do cpuset.

# mkdir cpu1

# cd /cpuset/cpu1/

# ls

cpu_exclusive
mem_exclusive
memory_pressure
memory_spread_slab
notify_on_release
tasks
cpus
memory_migrate
memory_spread_page
mems
sched_relax_domain_level

Os arquivos cpus, mems, tasks foram criados automaticamente, porém sem valor algum. É nesse ponto que iremos escolher qual CPU, área de memória (L2) e processos serão atribuídos para esse conjunto cpuset.

# cat /cpuset/cpu1/cpus
# cat /cpuset/cpu1/mems
# cat /cpuset/cpu1/tasks
# echo 1 > /cpuset/cpu1/cpus
# echo 0 > /cpuset/cpu1/mems
# cat /cpuset/cpu1/cpus

1
# cat /cpuset/cpu1/mems
0

Definimos acima que para esse conjunto cpuset iremos utilizar a CPU1 e a área de memória 0. Tudo que temos que fazer agora é enviar o(s) PID(s) do(s) processo(s) que queremos dedicar para a CPU1. Para exemplificar, irei pegar 2 processos: init e vsftpd e verificar qual processador foi atríbuido originalmente pelo scheduler:

# service vsftpd start

# ps axo pid,comm,psr | grep -e COMMAND -e init -e vsftpd
PID COMMAND         PSR
1 init                0
4328 vsftpd           0

Como podemos constatar, o scheduler atribuiu o CPU0 para as 2 tarefas. O que faremos agora é dedicar a CPU1 para os processos init e vsftpd.

# echo $(pidof init) > /cpuset/cpu1/tasks
# echo $(pidof vsftpd) > /cpuset/cpu1/tasks
# ps axo pid,comm,psr | grep -e COMMAND -e init -e vsftpd
PID COMMAND         PSR
1 init                1
4328 vsftpd           1

Pronto!!! Os processos init e vsftpd já estão rodando exclusivamente na CPU1. Outro recurso interessante do cpuset é que se o processo trabalhar com FORK, o processo filho irá herdar a cpu dedicada do processo pai, como no caso do vsftpd.

ftp localhost
Connected to localhost.localdomain.
220 (vsFTPd 2.0.5)
530 Please login with USER and PASS.
530 Please login with USER and PASS.
KERBEROS_V4 rejected as an authentication type
Name (localhost:marcelo): ftp
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

Em outro terminal:

# ps axo pid,comm,psr | grep -e COMMAND -e init -e vsftpd
PID COMMAND         PSR
1 init                1
4328 vsftpd           1
4352 vsftpd           1

A cada novo processo o scheduler sempre irá atribuir a CPU0, afinal é a única CPU disponível (lembrem-se que isolamos a CPU1 do scheduler com o parâmetro isolcpus=1). Assim sendo, é extremamente recomendado a criação de um script que obtenha o PID do processo à ser isolado e envie para o conjunto cpuset desejado. Outra consideração também é que os diretórios criados dentro do /cpuset não são persistentes e portanto precisam ser recriados a cada reboot.

Grande abraço!

Outras dicas deste autor

Tunando o sistema de arquivos - entendendo o journal do EXT3

Ativando e desativando CPUs sob demanda

Utilizando o RPM como ferramenta de recovery e auditoria

Atualizando o seu Fedora com o Preupgrade

Ataque de dicionário com OpenSSL - quebrando senhas

Leitura recomendada

Removendo kernel antigo no Fedora de forma simples

Manjaro não inicializa pelo GRUB de outra distribuição [Resolvido]

Compilando kernel 3.16.1 no Fedora 20

Removendo Kernels antigos do openSUSE

Desafio Linux (teste de segurança)

  

Comentários
[1] Comentário enviado por bristot em 06/11/2009 - 06:52h

Parabéns, ótimo artigo.

[2] Comentário enviado por cesar em 06/11/2009 - 14:52h

Nossa, legal!

Nunca tinha pensado nisso parabéns. ;)

[]'s



Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts