Linux下進程的CPU配置與線程綁定過程
1 基于進程的CPU配置
基于進程的CPU配置技術是指在Linux操作系統(tǒng)中,通過調整進程的CPU使用率來實現(xiàn)對系統(tǒng)資源的合理分配和管理。這種技術可以用于限制特定進程的CPU使用,防止其占用過多的CPU資源,從而保證系統(tǒng)的穩(wěn)定性和性能。
基于進程的CPU配置是指將一個進程綁定到特定的CPU核心或者CPU集合上運行。這樣可以控制進程在特定的CPU資源上執(zhí)行,以提高性能或實現(xiàn)特定的調度策略。
1.1 對CPU親和力的配置
對于CentOS 8下的Linux系統(tǒng),首先可以使用top命令來查看當前的CPU占用率,如下圖所示:
鍵盤上按數(shù)字1,可以以數(shù)據(jù)化的形式看到具體的使用情況,如下圖:
鍵盤上按字母t,可以以圖形化的形式看到具體的使用情況,如下圖:
可以看到,當前的CPU基本是空閑的狀態(tài)。
這時候,我們寫一個測試代碼,讓其掛在后臺運行,例如下面的一個死循環(huán)函數(shù):
#include <iostream> int main() { while (true) { // 在這里編寫你的代碼 // 例如,輸出一條信息 std::cout << "Hello, I am running in an infinite loop!" << std::endl; } return 0; }
讓其掛在后臺運行著,暫時不管。
如下圖:
此時,我們再次使用top命令,來查看當前的CPU 狀態(tài),如下圖:
可以看到,此時循環(huán)代碼這個進程正在后臺不停運行,此時的CPU占用率相比開始,非常的高。
記住當前的進程PID,然后使用taskset -c -p 13265 命令查看當前進程的CPU親和力。13265是當前進程的PID。
可以從輸出的信息看出,pid 為13265的進程(即當前的測試進程)的親和力CPU為0-3,即它同時運行在了0,1,2,3這四個CPU上面。
現(xiàn)在我們更改pid 為13265的進程(即當前的測試進程)的親和力,將其改成只在0,1這兩個CPU上面運行。如下圖:
這里由于我使用了ctrl + c暫停了剛才的進程,然后重新啟動該進程時,進程的PID變成了13495,不過這沒有任何影響??梢钥吹剑斍暗倪M程新的親和力列表為0,1,說明設置新的CPU親和力成功。而pid 13495 的當前親和力掩碼為3,同樣能說明親和力設置成功。使用taskset命令來查看進程運行在哪個CPU上。
使用以下命令:
taskset -p < PID >
【請將< PID >替換為你要查看的進程的實際PID?!?/p>
該命令將顯示進程的當前親和力掩碼,其中每個位表示一個CPU核心。例如,如果輸出為pid 's current affinity mask: 3,表示進程當前在CPU核心0和1上運行,因為二進制表示為11。
再次實驗一下,這次我們將CPU親和力設置在CPU 1,CPU2上,如下圖:
由當前親和力掩碼可以看到,為6,說明此時運行在CPU1和CPU2上。(2^1 + 2^2 = 6)
1.2 綁定進程到指定CPU核上運行
查看CPU有幾個核
使用 cat /proc/cpuinfo 查看CPU信息,如下兩個信息:
- ·processor:指明第幾個cpu處理器
- ·cpu cores:指明每個處理器的核心數(shù)
以本機中虛擬機為例,有4個CPU(分別為:CPU0, CPU1, CPU2, CPU3),每個CPU有1個核。
也可以使用系統(tǒng)調用sysconf獲取CPU核心數(shù):
#include <unistd.h> int sysconf(_SC_NPROCESSORS_CONF);/* 返回系統(tǒng)可以使用的核數(shù),但是其值會包括系統(tǒng)中禁用的核的數(shù)目,因 此該值并不代表當前系統(tǒng)中可用的核數(shù) */ int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系統(tǒng)當前可用的核數(shù) */ /* 以下兩個函數(shù)與上述類似 */ #include <sys/sysinfo.h> int get_nprocs_conf (void);/* 可用核數(shù) */ int get_nprocs (void);/* 真正的反映了當前可用核數(shù) */
使用 taskset 指令
- 獲取進程pid:
- 查看進程當前運行在哪個CPU上:
顯示的十六進制f轉換為二進制為最低四個是1,每個1對應一個CPU,所以進程運行在4個CPU上。
- 指定進程10770運行在CPU0上:
注意,CPU的標號是從0開始的,所以cpu0表示第1個CPU(第一個CPU的標號是0)。
至此,就把應用程序綁定到了CPU0上運行,查看如下:
- 啟動程序時綁定CPU:
例如啟動時綁定到第二個CPU上,即CPU1:
使用sched_setaffinity系統(tǒng)調用
通過系統(tǒng)調用sched_setaffinity進行綁定,通過sched_getaffinity獲取綁定關系。注意這對方法是進程級別的綁定。代碼中指定cpu0和cpu3,我們可以通過top查看,兩個CPU使用達到了100%,其他的CPU均不會(正常場景)。
sched_setaffinity可以將某個進程綁定到一個特定的CPU。
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <sched.h> /* 設置進程號為pid的進程運行在mask所設定的CPU上 * 第二個參數(shù)cpusetsize是mask所指定的數(shù)的長度 * 通常設定為sizeof(cpu_set_t) * 如果pid的值為0,則表示指定的是當前進程 */ int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);/* 獲得pid所指示的進程的CPU位掩碼,并將該掩碼返回到mask所指向的結構中 */
代碼示例:
/* *該程序演示了如何使用sched_setaffinity函數(shù)將線程綁定到特定的CPU核心上運行。 *程序首先創(chuàng)建了兩個線程,然后使用sched_setaffinity函數(shù)將線程1綁定到CPU 0上,將線程2綁定到CPU 3上。 *運行時,可以通過查看輸出的pid來確定程序的進程ID。 *然后,程序將CPU_ZERO宏應用于一個cpu_set_t類型的變量mask,以將其初始化為空集。 *接下來,程序將CPU_SET宏應用于mask,將CPU 0和CPU 3添加到集合中。 *最后,程序調用sched_setaffinity函數(shù)將mask應用于當前進程,將線程1綁定到CPU 0上,將線程2綁定到CPU 3上。 *線程創(chuàng)建成功后,程序使用pthread_join函數(shù)等待線程1和線程2的結束。 */ #define _GNU_SOURCE /* See feature_test_macros(7) */ #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sched.h> #include <pthread.h> void* testfunc(void* t) { while(1); return NULL; } int main() { cpu_set_t mask; // 定義cpu_set_t類型的變量mask,用于存儲CPU集合 printf("pid=%d\n", getpid()); // 打印進程ID CPU_ZERO(&mask); // 將mask初始化為空集 CPU_SET(0, &mask);//將cpu0綁定到mask中 CPU_SET(3, &mask);//將cpu3綁定到mask中 // 將mask應用于當前進程,綁定線程到指定的CPU核心 sched_setaffinity(0, sizeof(cpu_set_t), &mask) ; pthread_t tid1;//創(chuàng)建線程1 if (pthread_create(&tid1, NULL, (void *)testfunc, NULL) != 0) { fprintf(stderr, "thread create failed\n"); // 線程創(chuàng)建失敗,打印錯誤信息 return -1; } pthread_t tid2;//創(chuàng)建線程2 if (pthread_create(&tid2, NULL, (void *)testfunc, NULL) != 0) { fprintf(stderr, "thread create failed\n"); // 線程創(chuàng)建失敗,打印錯誤信息 return -1; } pthread_join(tid1, NULL); // 等待線程1結束 pthread_join(tid1, NULL); // 等待線程2結束 return 0; }
執(zhí)行結果如下圖所示:
- 執(zhí)行前:
- 執(zhí)行后:
2 基于線程的CPU配置
2.1 線程綁定:使用函數(shù)pthread_setaffinity_np
線程綁定CPU核心的意義:
- 在多核CPU中合理的調度線程在各個核上運行可以獲得更高的性能。
- 在多線程編程中,每個線程處理的任務優(yōu)先級是不一樣的,對于要求實時性比較高的線程或者是主線程,對于這種線程我們可以在創(chuàng)建線程時指定其綁定到某個CPU核上,以后這個核就專門處理該線程。
- 這樣可以使得該線程的任務可以得到較快的處理,特別是和用戶直接交互的任務,較短的響應時間可以提升用戶的體驗感。
幾個重要的宏操作:
一個線程的CPU親合力掩碼用一個cpu_set_t結構體來表示一個CPU集合,下面的幾個宏分別對這個掩碼集進行操作:
CPU_ZERO() 清空一個集合 CPU_SET()與CPU_CLR()分別對將一個給定的CPU號加到一個集合或者從一個集合中去掉 CPU_ISSET()檢查一個CPU號是否在這個集合中
設置獲取線程CPU親和力狀態(tài):
sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
該函數(shù)設置線程為pid的這個線程,讓它運行在mask所設定的CPU上。
如果pid的值為0,則表示指定的是當前線程,使當前線程運行在mask所設定的那些CPU上。
第二個參數(shù)cpusetsize是mask所指定的數(shù)的長度。通常設定為sizeof(cpu_set_t)。
如果當前pid所指定的線程此時沒有運行在mask所指定的任意一個CPU上,則該指定的線程會從其它CPU上遷移到mask的指定的一個CPU上運行。
sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
該函數(shù)獲得pid所指示的線程的CPU位掩碼,并將該掩碼返回到mask所指向的結構中。即獲得指定pid當前可以運行在哪些CPU上。同樣,如果pid的值為0。也表示的是當前進程。
簡單的實例:
// 此代碼不完整,只是幫助理解綁定過程 // 在創(chuàng)建線程時添加以下代碼,可以將該線程綁定到1核 cpu_set_t mask; // 將掩碼清零 CPU_ZERO(&mask); // 將1添加到掩碼中 CPU_SET(1, &mask); // #將本線程綁定到1核 sched_setaffinity(0, sizeof(cpu_set_t), &mask);
查看線程是否運行在指定的核上:
實際工作中,為了方便查看線程的情況,會在創(chuàng)建線程時將相關信息保存到一個文件中,需要時用cat命令查看,內容包括創(chuàng)建了哪些線程、線程名稱、線程id和pid、綁定的CPU核、優(yōu)先級、調度方式等。
使用“top”命令查看:
- top -d 2:查看線程的運行情況和CPU狀態(tài)
- 按’h’ 和 1:在上一句的基礎上可以查看更詳細的信息。
從文件中得到線程pid和ppid,通過top命令,查看線程在哪個CPU核上運行,驗證核綁定的核是否一樣。
對于線程綁定,我們需要借助pthread庫,通過函數(shù)pthread_setaffinity_np來設置綁定cpu關系。我們通過top查看,會發(fā)現(xiàn)cpu0和cpu3使用率達到100%。
代碼實例:
/* *該程序演示了如何使用pthread_setaffinity_np函數(shù)將線程綁定到特定的CPU核心上運行。 *程序首先創(chuàng)建了兩個線程,然后使用pthread_setaffinity_np函數(shù)將線程1綁定到CPU 0上,將線程2綁定到CPU 3上。 *運行時,可以通過查看輸出的pid來確定程序的進程ID。 *然后,程序將CPU_ZERO宏應用于一個cpu_set_t類型的變量mask,以將其初始化為空集。 *接下來,程序使用pthread_create函數(shù)創(chuàng)建線程1和線程2,并檢查線程創(chuàng)建是否成功。 *然后,程序打印出線程1和線程2的ID。 *程序使用CPU_SET宏將CPU 0添加到mask中,并使用pthread_setaffinity_np函數(shù)將mask應用于線程1,將線程1綁定到CPU 0上。 *然后,程序清除之前設置的mask,并將CPU 3添加到mask中,并使用pthread_setaffinity_np函數(shù)將mask應用于線程2,將線程2綁定到CPU 3上。 *最后,程序使用pthread_join函數(shù)等待線程1和線程2的結束。 */ #define _GNU_SOURCE /* See feature_test_macros(7) */ #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sched.h> #include <pthread.h> void* testfunc(void* t) { int i = 3; // 初始化循環(huán)計數(shù)器為3 while(i) { // 進入循環(huán),條件為i非零 sleep(5); // 休眠5秒 printf("tid=%d,cpu=%d\n",pthread_self(), sched_getcpu()); // 打印線程ID和CPU編號 i--; // 計數(shù)器減一 } while(1); // 進入無限循環(huán) return NULL; } int main() { cpu_set_t mask; // 定義CPU集合 printf("pid=%d\n", getpid()); // 打印進程ID CPU_ZERO(&mask); // 清空CPU集合 pthread_t tid1; // 定義線程tid1 if (pthread_create(&tid1, NULL, (void *)testfunc, NULL) != 0) { fprintf(stderr, "thread create failed\n"); // 線程創(chuàng)建失敗,打印錯誤信息 return -1; } pthread_t tid2; // 定義線程tid2 if (pthread_create(&tid2, NULL, (void *)testfunc, NULL) != 0) { fprintf(stderr, "thread create failed\n"); // 線程創(chuàng)建失敗,打印錯誤信息 return -1; } printf("tid1=%d,tid2=%d\n", tid1,tid2); // 打印線程tid1和tid2的值 CPU_SET(0, &mask); // 將CPU0加入CPU集合 pthread_setaffinity_np(tid1, sizeof(cpu_set_t), &mask) ; // 設置線程tid1的CPU親和性為CPU0 // 清除之前設置,重新設置綁定cpu3 CPU_ZERO(&mask); // 清空CPU集合 CPU_SET(3, &mask); // 將CPU3加入CPU集合 pthread_setaffinity_np(tid2, sizeof(cpu_set_t), &mask) ; // 設置線程tid2的CPU親和性為CPU3 pthread_join(tid1, NULL); // 等待線程tid1結束 pthread_join(tid1, NULL); // 等待線程tid2結束 return 0; }
- 執(zhí)行之后:
- 將其kill,恢復:
建議:進行配置之前先將虛擬機拍攝快照,以防配置不當出現(xiàn)意外情況。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Linux網絡啟動問題:Device does not seem to be present解決辦法
這篇文章主要介紹了Linux網絡啟動問題:Device does not seem to be present解決辦法的相關資料,希望通過本文能幫助到大家解決這樣的問題,需要的朋友可以參考下2017-10-10Linux內核設備驅動之proc文件系統(tǒng)筆記整理
今天小編就為大家分享一篇關于Linux內核設備驅動之proc文件系統(tǒng)筆記整理,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12