Linux:設置進程(線程)的CPU親和性
一、進程的CPU親和性的獲取(get)或者設置(set)
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);
demo:主線程創建四個線程,獲取主線程的CPU親和性,設置四個非主線程的CPU親和性
main.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/syscall.h>
pid_t gettid() {
return syscall(SYS_gettid);
}
void *start_routine(void *arg)
{
int thn = *((int *)arg);
pid_t pid = gettid(); // LWP
pthread_t tid = pthread_self();
long cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
if (cpu_num <= 0) {
printf("thn=%d,pid=%d,tid=%lu,get cpu info error:%ld\n",
thn, pid, tid, cpu_num);
return;
}
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(thn%cpu_num, &cpu_set);
int res = sched_setaffinity(0 /* or pid */, sizeof(cpu_set), &cpu_set);
if (res != 0) {
printf("thn=%d,pid=%d,tid=%lu,sched_setaffinity error:%d(%s)\n",
thn, pid, tid, res, strerror(res));
return;
}
printf("thn=%d,pid=%d,tid=%lu,bind cpu[%d] ok!\n",
thn, pid, tid, thn%cpu_num);
while (1)
sleep(1);
}
int main()
{
int i0 = 0;
pthread_t tid0;
pthread_create(&tid0, NULL, start_routine, &i0);
int i1 = 1;
pthread_t tid1;
pthread_create(&tid1, NULL, start_routine, &i1);
int i2 = 2;
pthread_t tid2;
pthread_create(&tid2, NULL, start_routine, &i2);
int i3 = 3;
pthread_t tid3;
pthread_create(&tid3, NULL, start_routine, &i3);
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
int res = sched_getaffinity(0 /* or getpid() */, sizeof(cpu_set), &cpu_set);
if (res != 0) {
printf("main: pid=%d,tid=%lu,sched_getaffinity error:%d(%s)\n",
getpid(), pthread_self(), res, strerror(res));
return;
}
int i = 0;
for (; i < sizeof(cpu_set); i++) {
if (CPU_ISSET(i, &cpu_set)) {
printf("main: pid=%d,tid=%lu,bind cpu[%d]\n", getpid(), pthread_self(), i);
}
}
while (1)
sleep(1);
}
編譯運行:
[test1280@localhost 20190305]$ gcc -o main main.c -lpthread
[test1280@localhost 20190305]$ ./main
main: pid=31387,tid=140119163442944,bind cpu[0]
thn=3,pid=31391,tid=140119131965184,bind cpu[3] ok!
main: pid=31387,tid=140119163442944,bind cpu[1]
main: pid=31387,tid=140119163442944,bind cpu[2]
main: pid=31387,tid=140119163442944,bind cpu[3]
thn=1,pid=31389,tid=140119152944896,bind cpu[1] ok!
thn=0,pid=31388,tid=140119163434752,bind cpu[0] ok!
thn=2,pid=31390,tid=140119142455040,bind cpu[2] ok!
^C
主線程(PID=31387)創建了四個線程(PID=31388、31389、31390、31391)。
PID=31387 可運行在 CPU0、CPU1、CPU2、CPU3
PID=31388 可運行在 CPU0
PID=31389 可運行在 CPU1
PID=31390 可運行在 CPU2
PID=31391 可運行在 CPU3
[test1280@localhost 20190305]$ taskset -pc 31387
pid 31387's current affinity list: 0-3
[test1280@localhost 20190305]$ taskset -pc 31391
pid 31391's current affinity list: 3
[test1280@localhost 20190305]$ taskset -pc 31389
pid 31389's current affinity list: 1
[test1280@localhost 20190305]$ taskset -pc 31388
pid 31388's current affinity list: 0
[test1280@localhost 20190305]$ taskset -pc 31390
pid 31390's current affinity list: 2
通過 taskset 命令可進行驗證。
關於 cpu_set_t 類型,類似於 fd_set(select),有幾個宏可直接對“位集”進行操作:
void CPU_ZERO(cpu_set_t *set);
void CPU_SET(int cpu, cpu_set_t *set);
void CPU_CLR(int cpu, cpu_set_t *set);
int CPU_ISSET(int cpu, cpu_set_t *set);
……
The affinity mask is actually a per-thread attribute that can be adjusted independently for each of the threads in a thread group.
CPU親和性是線程級屬性,能夠被獨立地(與其他線程無關)地設置或獲取。
線程是最小的調度執行單元,理所應當地,是線程運行在某CPU之上,親和性是線程的一個屬性。
需要注意,這兩個系統調用的第一個參數pid,都是內核分配的PID(TID、LWP),而非 pthread_t 類型。
準確地說,沒有能設置、獲取整個進程的CPU親和性的接口,只有能設置、獲取某線程的CPU親和性的接口。
二、線程的CPU親和性的獲取(get)或者設置(set)
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);
demo:主線程創建四個線程,獲取主線程的CPU親和性,設置四個非主線程的CPU親和性
main.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/syscall.h>
pid_t gettid() {
return syscall(SYS_gettid);
}
void *start_routine(void *arg)
{
int thn = *((int *)arg);
pid_t pid = gettid(); // LWP
pthread_t tid = pthread_self();
long cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
if (cpu_num <= 0) {
printf("thn=%d,pid=%d,tid=%lu,get cpu info error:%ld\n",
thn, pid, tid, cpu_num);
return;
}
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(thn%cpu_num, &cpu_set);
int res = pthread_setaffinity_np(tid, sizeof(cpu_set), &cpu_set);
if (res != 0) {
printf("thn=%d,pid=%d,tid=%lu,pthread_setaffinity_np error:%d(%s)\n",
thn, pid, tid, res, strerror(res));
return;
}
printf("thn=%d,pid=%d,tid=%lu,bind cpu[%d] ok!\n",
thn, pid, tid, thn%cpu_num);
while (1)
sleep(1);
}
int main()
{
int i0 = 0;
pthread_t tid0;
pthread_create(&tid0, NULL, start_routine, &i0);
int i1 = 1;
pthread_t tid1;
pthread_create(&tid1, NULL, start_routine, &i1);
int i2 = 2;
pthread_t tid2;
pthread_create(&tid2, NULL, start_routine, &i2);
int i3 = 3;
pthread_t tid3;
pthread_create(&tid3, NULL, start_routine, &i3);
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
int res = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
if (res != 0) {
printf("main: pid=%d,tid=%lu,pthread_getaffinity_nperror:%d(%s)\n",
getpid(), pthread_self(), res, strerror(res));
return;
}
int i = 0;
for (; i < sizeof(cpu_set); i++) {
if (CPU_ISSET(i, &cpu_set)) {
printf("main: pid=%d,tid=%lu,bind cpu[%d]\n", getpid(), pthread_self(), i);
}
}
while (1)
sleep(1);
}
運行輸出:
[test1280@localhost 20190305]$ ./main
main: pid=31765,tid=140313705346816,bind cpu[0]
main: pid=31765,tid=140313705346816,bind cpu[1]
main: pid=31765,tid=140313705346816,bind cpu[2]
main: pid=31765,tid=140313705346816,bind cpu[3]
thn=0,pid=31766,tid=140313705338624,bind cpu[0] ok!
thn=1,pid=31767,tid=140313694848768,bind cpu[1] ok!
thn=3,pid=31769,tid=140313673869056,bind cpu[3] ok!
thn=2,pid=31768,tid=140313684358912,bind cpu[2] ok!
^C
[test1280@localhost 20190305]$ taskset -pc 31765
pid 31765's current affinity list: 0-3
[test1280@localhost 20190305]$ taskset -pc 31766
pid 31766's current affinity list: 0
[test1280@localhost 20190305]$ taskset -pc 31767
pid 31767's current affinity list: 1
[test1280@localhost 20190305]$ taskset -pc 31768
pid 31768's current affinity list: 2
[test1280@localhost 20190305]$ taskset -pc 31769
pid 31769's current affinity list: 3
pthread_setaffinity_np 以及 pthread_getaffinity_np 是 libpthread 庫(man 3) 實現的接口,底層實現基於系統調用(man 2) sched_setaffinity 以及 sched_setscheduler。
man pthread_getaffinity_np
These functions are implemented on top of the sched_setaffinity(2) and sched_getaffinity(2) system calls.
既然是 libpthread 庫實現的接口,使用的線程標識符也是 libpthread 庫自定義的 pthread_t 類型,而非 pid_t。
libpthread 庫實現的這兩個接口是不可移植的,所以函數名帶了 _np 後綴標識(non-portable)。
man pthread_getaffinity_np
These functions are non-standard GNU extensions; hence the suffix "_np" (non-portable) in the names.
通過 pthread_create 創建的線程將 繼承 原線程的CPU親和性屬性值。
man pthread_getaffinity_np
A new thread created by pthread_create() inherits a copy of its creator’s CPU affinity mask.
參考:
1.https://www.cnblogs.com/wenqiang/p/6049978.html
2.https://www.cnblogs.com/LubinLew/p/cpu_affinity.html
相關:
1.https://blog.csdn.net/test1280/article/details/87993669
2.https://blog.csdn.net/test1280/article/details/87991302
3.https://blog.csdn.net/test1280/article/details/87974748