在 Linux 中以特定的 CPU 核心執行程式

這裡介紹如何在 Linux 中以特定的 CPU 核心執行程式,不要讓系統自動排程。


現在不管是伺服器或是一般個人電腦的 CPU 大部分都是多核心的架構,而各種應用軟體、編譯器與作業系統也受到這個趨勢的影響,也都會有針對多核心處理器做的最佳化設計。

通常在多核心的作業系統中常使用處理器的親和性(processor affinity,亦稱 CPU pinning)來處理需要高效能計算的應用,這個技術是屬於作業系統的一個特殊功能,它可以讓行程在特定的 CPU 核心中持續執行,不受作業系統排程的干擾。

將行程綁定在特定的 CPU 核心上有許多優點,例如一個 cache bound 的程式跟其他比較耗費 CPU 計算的程式一起執行時,將程式綁定在特定的 CPU 核心可以減少 cache miss 的狀況。另外在兩個行程頻繁的藉由 shared memory 進行溝通時,將兩個行程都綁定在同一個 NUMA 節點中也可以增進執行效率。

這裡我們會以 Linux 系統為例,介紹如何在 Linux 系統中將一般的程式綁定在一個選定的處理器核心中執行。

在 Linux 系統中若要將特定的處理器核心指定給一個程式或行程使用,可以使用 taskset,透過這個指令使用者就可以設定或取得行程的處理器親和性。

安裝 taskset

在 Linux 中 taskset 是屬於 util-linux 套件中的一部分,但是大部分的 Linux 發行版預設都會安裝,所以通常不需要自己動手裝。如果你的系統沒有安裝這個套件,可以按照下面的方式安裝。

Debian 系列的 Linux(Ubuntu、Linux Mint 等)可用 apt 安裝:
sudo apt-get install util-linux

Red Hat 系列的 Linux(Fedora、CentOS 等),則可使用 yum:
sudo yum install util-linux

查看行程(Process)的處理器親和性(CPU affinity)

若要查看指定行程的處理器親和性,可以使用 taskset 加上 -p 參數再加上行程的 ID(process ID):
taskset -p PID
其中 PID 就是行程的 ID,例如:
taskset -p 2915
輸出為:
pid 2915's current affinity mask: ff
輸出的 affinity mask 是一個十六進位的 bitmask,將其轉換為二進位格式之後,若位元值為 1 則代表該行程可以在這個位元對應的 CPU 核心中執行,若位元值為 0 則代表該行程不允許在這個位元對應的 CPU 核心中執行。

在上面這個例子中十六進位的 ff 轉成二進位的格式會是 11111111,這八個 1 分別代表該行程可以在第 0 到第 7 個 CPU 核心中執行,最低(最右邊)的位元代表第 0 個 CPU 核心,次低的代表第 1 個,以次類推。

如果 affinity mask 是一個 0x11,則代表可在第 0 個與第 4 個 CPU 核心執行。

如果感覺以 bitmask 表示法不容易理解,可以加上 -c 參數,讓 taskset 直接輸出 CPU 的核心列表:
taskset -cp 2915
輸出為:
pid 2915's current affinity list: 0-7

將行程固定在特定的 CPU 核心中執行

taskset 亦可設定行程的 core mask,將指定的行程固定在特定的 CPU 核心中執行:
taskset -p COREMASK PID
其中 COREMASK 就是指定的十六進位 core mask,PID 則為行程的 ID。除此之外,亦可使用 -c 參數以 CPU 的核心列表來指定:
taskset -cp CORELIST PID
其中 CORELIST 為 CPU 核心列表,以逗點分個各個核心的編號或是使用連字號指定連續的區間,例如:0,2,5,7-10

例如若要將一個行程固定在第 0 個與第 4 個 CPU 核心,則使用:
taskset -p 0x11 9030
輸出為:
pid 9030's current affinity mask: ff
pid 9030's new affinity mask: 11
亦可使用 CPU 核心列表的方式:
taskset -cp 0,4 9030
兩種方式都是一樣的。

在 Linux 中的使用者必須有開啟 CAP_SYS_NICE 這個權限,才能更動行程的處理器親和性,而若只是要查看處理器親和性的設定,則沒有限制(任何使用者皆可查詢)。

以特定的 CPU 核心執行程式

除了更改現有行程的處理器親和性,使用者也可以使用 taskset 指定 CPU 核心來執行一個新的程式:
taskset COREMASK EXECUTABLE
其中 EXECUTABLE 是要執行的程式。
例如若要以第 0 個 CPU 核心執行 vlc 則使用:
taskset 0x1 vlc

將 CPU 核心限定給特定的程式使用

taskset 可以指定行程所使用的 CPU 核心,但是不代表其他的行程不會使用這些被指定的 CPU 核心,如果你想讓其他的行程干擾你要執行的程式,讓指定的核心只能被自己設定的行程使用,可以使用 isolcpus 這個 Linux 核心(kernel)參數,這個參數可以讓特定的 CPU 核心在開機時就被保留下來。

設定的方式有兩種,一個是在開機時使用 boot loader 所提供的自訂開機參數功能,手動加入 isolcpus=cpu_id 參數,或是直接加在 GRUB 的設定檔中,這樣 Linux 的排程器(scheduler)在預設的狀況下就不會將任何一般行程放在這個被保留的 CPU 核心中執行,只有那些被 taskset 特別指定的行程可以使用這些 CPU 核心。

舉例來說,如果想讓第 0 個與第 1 個 CPU 核心都被保留下來,則在開機時加入
isolcpus=0,1
這個 Linux 核心參數,然後再使用 taskset 指令將這兩個 CPU 核心指定給要執行的程式使用即可。

參考資料:Xmodulo
本站已經搬家了,欲查看最新的文章,請至 G. T. Wang 新網站