6

Dedicando uma CPU para processos específicos

Primeiramente, gostaria de agradecer a todos que têm me enviado emails solicitando posts e parabenizando o blog. Como prometido, nesse post irei demonstrar como podemos dedicar uma cpu para processos específicos.
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 cpu’s isolados. 
No laptop que estou utilizando, tenho1 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.
[root@mmello ~]# 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 
 [root@mmello ~]# 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.
[root@mmello ~]# 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&nbs
p;       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
[root@mmello ~]#
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. 
[root@mmello ~]# mkdir /cpuset
[root@mmello ~]# mount -t cpuset none /cpuset/
[root@mmello ~]# mount| grep cpuset
none on /cpuset type cpuset (rw)
[root@mmello ~]# 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
[root@mmello ~]#

    Cada conjunto cpuset é representado por um diretório do tipo cpuset que contém os alguns arquivos, dentre eles os principais: 
          * cpus: lista de CPU’s 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.

    [root@mmello cpuset]# cat /cpuset/mems
    0
    [root@mmello cpuset]# cat /cpuset/cpus
    0-1
    [root@mmello cpuset]# wc -l /cpuset/tasks
    170 /cpuset/tasks
    [root@mmello cpuset]# 

    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.
    [root@mmello cpuset]# mkdir cpu1
    [root@mmello cpuset]# cd /cpuset/cpu1/
    [root@mmello 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
    [root@mmello cpu1]#

    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. 
    [root@mmello cpu1]# cat /cpuset/cpu1/cpus
    [root@mmello cpu1]# cat /cpuset/cpu1/mems
    [root@mmello cpu1]# cat /cpuset/cpu1/tasks
    [root@mmello cpu1]# echo 1 > /cpuset/cpu1/cpus
    [root@mmello cpu1]# echo 0 > /cpuset/cpu1/mems
    [root@mmello cpu1]# cat /cpuset/cpu1/cpus
    1
    [root@mmello cpu1]# cat /cpuset/cpu1/mems
    0
    [root@mmello cpu1]#


    Defimos 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. 
     [root@mmello cpu1]# service vsftpd start
    Starting vsftpd for vsftpd:                                [  OK  ]
    [root@mmello cpu1]# ps axo pid,comm,psr | grep -e COMMAND -e init -e vsftpd
      PID COMMAND         PSR
        1 init                    0
     4328 vsftpd            0
    [root@mmello cpu1]#

    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.
    [root@mmello cpu1]# echo $(pidof init) > /cpuset/cpu1/tasks
    [root@mmello cpu1]# echo $(pidof vsftpd) > /cpuset/cpu1/tasks
    [root@mmello cpu1]# ps axo pid,comm,psr | grep -e COMMAND -e init -e vsftpd
      PID COMMAND         PSR
        1 init                    1
     4328 vsftpd            1
    [root@mmello cpu1]#


    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.
    [marcelo@mmello ~]$ 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:

    [root@mmello cpu1]# 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!!!!!