觀察發現4核CPU,只有第1個核心(CPU#0)非常忙,其他都處於idle狀態。
不了解Linux是如何調度的,但目前顯然有優化的余地。除了處理正常任務,CPU#0還需要處理每秒網卡中斷。因此,若能將CPU#0分擔的任務攤派到其他CPU核心上,可以預見,系統的處理能力將有更大的提升。
兩個名詞
SMP (Symmetrical Multi-Processing):指在一個計算機上匯集了一組處理器(多CPU),各CPU之間共享內存子系統以及總線結構。SMP意為對稱多處理系統,內有許多緊耦合多處理器,這種系統的最大特點就是共享所有資源。另外與之相對立的標准是MPP (Massively Parallel Processing),意為大規模並行處理系統,這樣的系統是由許多松耦合處理單元組成的,要注意的是這里指的是處理單元而不是處理器。每個單元內的CPU都有自己私有的資源,如總線、內存、硬盤等。在每個單元內都有操作系統和管理數據庫的實例復本。這種結構最大的特點在於不共享資源 [更多...]
CPU affinity:中文喚作“CPU親和力”,是指在CMP架構下,能夠將一個或多個進程綁定到一個或多個處理器上運行。[更多...]
一、在Linux上修改進程的“CPU親和力”
在Linux上,可以通過 taskset 命令進行修改,安裝taskset工具。
# yum install util-linux-ng
對運行中的進程,文檔上說可以用下面的命令,把CPU#1 #2 #3分配給PID為2345的進程:
# taskset -cp 1,2,3 2345
但我嘗試沒奏效,於是我關掉了MySQL,並用taskset將它啟動:
# taskset -c 1,2,3 /etc/init.d/mysql start
對於其他進程,也可如此處理(nginx除外,詳見下文)。之后用top查看CPU的使用情況,原來空閑的#1 #2 #3,已經在辛勤工作了。
二、配置nginx綁定CPU
剛才說nginx除外,是因為nginx提供了更精確的控制。
在conf/nginx.conf中,有如下一行:
worker_processes 1;
這是用來配置nginx啟動幾個工作進程的,默認為1。而nginx還支持一個名為worker_cpu_affinity的配置項,也就是說,nginx可以為每個工作進程綁定CPU。我做了如下配置:
worker_processes 3;
worker_cpu_affinity 0010 0100 1000;
這里0001、0010、0100、1000是掩碼,分別代表第1、2、3、4顆cpu核心。
重啟nginx后,3個工作進程就可以各自用各自的CPU了。
三、刨根問底
- 如果自己寫代碼,要把進程綁定到CPU,該怎么做?可以用sched_setaffinity函數。在Linux上,這會觸發一次系統調用。
- 如果父進程設置了affinity,之后其創建的子進程是否會有同樣的屬性?我發現子進程確實繼承了父進程的affinity屬性。
四、Windows?
在Windows上修改“CPU親和力”,可以通過任務管理器搞定。
* 個人感覺,Windows系統中翻譯的“處理器關系”比“CPU親和力”容易理解點兒
—————–
進行了這樣的修改后,即使系統負載達到3以上,不帶緩存打開blogkid.net首頁(有40多次查詢)依然順暢;以前一旦負載超過了1.5,響應就很慢了。效果很明顯。
linux taskset命令詳解
SYNOPSIS
taskset [options] [mask | list ] [pid | command [arg]...]
OPTIONS
-p, --pid
operate on an existing PID and not launch a new task
-c, --cpu-list
specifiy a numerical list of processors instead of a bitmask.
The list may contain multiple items, separated by comma, and
ranges. For example, 0,5,7,9-11.
-h, --help
display usage information and exit
-V, --version
output version information and exit
# taskset -p -c 0 16380 或者 taskset -cp 0 16380
# ps -e -o psr,pid,cmd |grep 29165 查驗
psr : 進程當前被分配的處理器
---------------------------------------------------------------
使CPU隔離,專門用於某個服務或進程:
為客戶提供一個有2個邏輯CPU計算能力的一個客戶機。要求CPU資源獨立被占用,不受宿主機中其他客戶機的負載水平的影響。為了滿足這個需求,可以分為如下兩個步驟來實現。
第一步,啟動宿主機時隔離出兩個邏輯CPU專門供一個客戶機使用。在Linux內核啟動的命令行加上“isolcpus=”參數,可以實現CPU的隔離,讓系統啟動后普通進程默認都不會調度到被隔離的CPU上執行。例如,隔離了cpu2和cpu3的grub的配置文件如下:
title Red Hat Enterprise Linux Server (3.5.0)
root (hd0,0)
kernel /boot/vmlinuz-3.5.0 ro root=UUID=1a65b4bb-cd9b-4bbf-97ff-7e1f7698d3db isolcpus=2,3
initrd /boot/initramfs-3.5.0.img
系統啟動后,在宿主機中檢查是否隔離成功,命令行如下:
[root@jay-linux ~]# ps -eLo psr | grep 0 | wc -l
106
[root@jay-linux ~]# ps -eLo psr | grep 1 | wc -l
107
[root@jay-linux ~]# ps -eLo psr | grep 2 | wc -l
4
[root@jay-linux ~]# ps -eLo psr | grep 3 | wc -l
4
[root@jay-linux ~]# ps -eLo ruser,pid,ppid,lwp,psr,args | awk ‘{if($5==2) print $0}’
root 10 2 10 2 [migration/2]
root 11 2 11 2 [kworker/2:0]
root 12 2 12 2 [ksoftirqd/2]
root 245 2 245 2 [kworker/2:1]
[root@jay-linux ~]# ps –eLo ruser,pid,ppid,lwp,psr,args | awk ‘{if($5==3) print $0}’
root 13 2 13 3 [migration/3]
root 14 2 14 3 [kworker/3:0]
root 15 2 15 3 [ksoftirqd/3]
root 246 2 246 3 [kworker/3:1]
從上面的命令行輸出信息可知,cpu0和cpu1上分別有106和107個線程在運行,而cpu2和cpu3上都分別只有4個線程在運行。而且,根據輸出信息中cpu2和cpu3上運行的線程信息(也包括進程在內),分別有migration進程(用於進程在不同CPU間遷移)、兩個kworker進程(用於處理workqueues)、ksoftirqd進程(用於調度CPU軟中斷的進程),這些進程都是內核對各個CPU的一些守護進程,而沒有其他的普通進程在cup2和cpu3上運行,說明對其的隔離是生效的。
另外,簡單解釋一下上面的一些命令行工具及其參數的意義。ps命令顯示當前系統的進程信息的狀態,它的“-e”參數用於顯示所有的進程,“-L”參數用於將線程(LWP,light-weight process)也顯示出來,“-o”參數表示以用戶自定義的格式輸出(其中“psr”這列表示當前分配給進程運行的處理器編號,“lwp”列表示線程的ID,“ruser”表示運行進程的用戶,“pid”表示進程的ID,“ppid”表示父進程的ID,“args”表示運行的命令及其參數)。結合ps和awk工具的使用,是為了分別將在處理器cpu2和cpu3上運行的進程打印出來。
第二步,啟動一個擁有2個vCPU的客戶機並將其vCPU綁定到宿主機中兩個CPU上。此操作過程的命令行如下:
#(啟動一個客戶機)
[root@jay-linux kvm_demo]# qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
VNC server running on ‘::1:5900’
#(查看代表vCPU的QEMU線程)
[root@jay-linux ~]# ps -eLo ruser,pid,ppid,lwp,psr,args | grep qemu | grep -v grep
root 3963 1 3963 0 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
root 3963 1 3967 0 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
root 3963 1 3968 1 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 –daemonize
#(綁定代表整個客戶機的QEMU進程,使其運行在cpu2上)
[root@jay-linux ~]# taskset -p 0×4 3963
pid 3963′s current affinity mask: 3
pid 3963′s new affinity mask: 4
#(綁定第一個vCPU的線程,使其運行在cpu2上)
[root@jay-linux ~]# taskset -p 0×4 3967
pid 3967′s current affinity mask: 3
pid 3967′s new affinity mask: 4
#(綁定第二個vCPU的線程,使其運行在cpu3上)
[root@jay-linux ~]# taskset -p 0×8 3968
pid 3968′s current affinity mask: 4
pid 3968′s new affinity mask: 8
#(查看QEMU線程的綁定是否生效,如下的第5列為處理器親和性)
[root@jay-linux ~]# ps -eLo ruser,pid,ppid,lwp,psr,args | grep qemu | grep -v grep
root 3963 1 3963 2 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
root 3963 1 3967 2 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
root 3963 1 3968 3 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 –daemonize
#(執行vCPU的綁定后,查看在cpu2上運行的線程)
[root@jay-linux ~]# ps -eLo ruser,pid,ppid,lwp,psr,args | awk ‘{if($5==2) print $0}’
root 10 2 10 2 [migration/2]
root 11 2 11 2 [kworker/2:0]
root 12 2 12 2 [ksoftirqd/2]
root 245 2 245 2 [kworker/2:1]
root 3963 1 3963 2 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
root 3963 1 3967 2 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
#(執行vCPU的綁定后,查看在cpu3上運行的線程)
[root@jay-linux ~]# ps –eLo ruser,pid,ppid,lwp,psr,args | awk ‘{if($5==3) print $0}’
root 13 2 13 3 [migration/3]
root 14 2 14 3 [kworker/3:0]
root 15 2 15 3 [ksoftirqd/3]
root 246 2 246 3 [kworker/3:1]
root 3963 1 3968 3 qemu-system-x86_64 rhel6u3.img -smp 2 -m 512 -daemonize
由上面的命令行及其輸出信息可知,CPU綁定之前,代表這個客戶機的QEMU進程和代表各個vCPU的QEMU線程分別被調度到cpu0和cpu1上。使用taskset命令將QEMU進程和第一個vCPU的線程綁定到cpu2,將第二個vCPU線程綁定到cpu3上。綁定之后,即可查看到綁定的結果是生效的,代表兩個vCPU的QEMU線程分別運行在cpu2和cpu3上(即使再過一段時間后,它們也不會被調度到其他CPU上去)。
對taskset命令解釋一下,此處使用的語法是:taskset -p [mask] pid 。其中,mask是一個代表了處理器親和性的掩碼數字,轉化為二進制表示后,它的值從最低位到最高位分別代表了第一個邏輯CPU到最后一個邏輯CPU,進程調度器可能將該進程調度到所有標為“1”的位代表的CPU上去運行。根據上面的輸出,taskset運行之前,QEMU線程的處理器親和性mask值是0×3(其二進制值為:0011),可知其可能會被調度到cpu0和cpu1上運行;而運行“taskset -p 0×4 3967”命令后,提示新的mask值被設為0×4(其二進制值為:0100),所以該進程就只能被調度到cpu2上去運行,即通過taskset工具實現了vCPU進程綁定到特定的CPU上。
上面命令行中,根據ps命令可以看到QEMU的線程和進程的關系,但如何查看vCPU與QEMU線程之間的關系呢?可以切換(“Ctrl+Alt+2”快捷鍵)到QEMU monitor中進行查看,運行“info cpus”命令即可(還記得3.6節中運行過的“info kvm”命令吧),其輸出結果如下:
(qemu) info cpus
* CPU #0: pc=0xffffffff810375ab thread_id=3967
CPU #1: pc=0xffffffff812b2594 thread_id=3968
從上面的輸出信息可知,客戶機中的cpu0對應的線程ID為3967,cpu1對應的線程ID為3968。另外,“CPU #0”前面有一個星號(*),是標識cpu0是BSP(Boot Strap Processor,系統最初啟動時在SMP生效前使用的CPU)。
總的來說,在KVM環境中,一般並不推薦手動地人為設置QEMU進程的處理器親和性來綁定vCPU,但是,在非常了解系統硬件架構的基礎上,根據實際應用的需求,是可以將其綁定到特定的CPU上去從而提高客戶機中的CPU執行效率或者實現CPU資源獨享的隔離性。
CPU中斷請求隔離方法:
應該將中斷綁定至那些非隔離的CPU上,從而避免那些隔離的CPU處理中斷程序:
echo CPU_MASK > /proc/irq/ <irq number>/smp_affinity
[root@lopdb1 ~]# cat /proc/irq/2/smp_affinity
7fffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff
1. 不同的設備一般都有自己的IRQ號碼(當然一個設備還有可能有多個IRQ號碼)
通過命令:cat /proc/interrupts查看
如:cat /proc/interrupts | grep -e “CPU\|eth4″
2. 中斷的smp affinity在cat /proc/irq/$Num/smp_affinity
可以echo “$bitmask” > /proc/irq/$num/smp_affinity來改變它的值。
注意smp_affinity這個值是一個十六進制的bitmask,它和cpu No.序列的“與”運算結果就是將affinity設置在那個(那些)CPU了。(也即smp_affinity中被設置為1的位為CPU No.)
比如:我有8個邏輯core,那么CPU#的序列為11111111 (從右到左依次為#0~#7的CPU)
如果cat /proc/irq/84/smp_affinity的值為:20(二進制為:00100000),則84這個IRQ的親和性為#5號CPU。
每個IRQ的默認的smp affinity在這里:cat /proc/irq/default_smp_affinity
另外,cat /proc/irq/$Num/smp_affinity_list 得到的即是CPU的一個List。
3. 默認情況下,有一個irqbalance在對IRQ進行負載均衡,它是/etc/init.d/irqbalance
在某些特殊場景下,可以根據需要停止這個daemon進程。
4. 如果要想提高性能,將IRQ綁定到某個CPU,那么最好在系統啟動時,將那個CPU隔離起來,不被scheduler通常的調度。
可以通過在Linux kernel中加入啟動參數:isolcpus=cpu-list來將一些CPU隔離起來。
參考文檔:
https://www.kernel.org/doc/Documentation/IRQ-affinity.txt