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