Linux:獲取、設置進程(線程)的CPU親和性

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章