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!