全文轉(zhuǎn)載自 CPU綁定技術(shù),侵刪。
概念
什么是CPU Affinity?Affinity 是進(jìn)程的一個(gè)屬性,這個(gè)屬性指明了進(jìn)程調(diào)度器能夠把這個(gè)進(jìn)程調(diào)度到哪些CPU上。
在Linux中,我們可以利用 CPU affinity 把一個(gè)或多個(gè)進(jìn)程綁定到一個(gè)或多個(gè)CPU上。CPU Affinity分為2種:
- soft affinity 僅是一個(gè)建議,如果不可避免,調(diào)度器還是會把進(jìn)程調(diào)度到其它的CPU上。
- hard affinity 是調(diào)度器必須遵守的規(guī)則。
為什么需要CPU綁定
增加CPU緩存的命中率
CPU之間是不共享緩存的,如果進(jìn)程頻繁的在各個(gè) CPU 間進(jìn)行切換,需要不斷的使舊 CPU 的 cache 失效。如果進(jìn)程只在某個(gè)CPU上執(zhí)行,則不會出現(xiàn)失效的情況。適合time-sensitive應(yīng)用
在real-time或time-sensitive應(yīng)用中,我們可以把系統(tǒng)進(jìn)程綁定到某些CPU上,把應(yīng)用進(jìn)程綁定到剩余的CPU上。典型的設(shè)置是,把應(yīng)用綁定到某個(gè)CPU上,把其它所有的進(jìn)程綁定到其它的CPU上。
綁定進(jìn)程和CPU
使用taskset指令
程序內(nèi)部使用代碼
#define _GNU_SOURCE
#include <sched.h>
long sched_setaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);
long sched_getaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);
從函數(shù)名以及參數(shù)名都很明了,唯一需要解釋的是第三個(gè)參數(shù), 這個(gè)參數(shù) select 中的 fd_set 比較類似,每個(gè)bit代表一個(gè)CPU。
- 設(shè)置 affinity 的例子
unsigned long mask = 7; /* processors 0, 1, and 2 */ unsigned int len = sizeof(mask); if (sched_setaffinity(pid, len, &mask) < 0) perror("sched_setaffinity"); - 獲取 affinity 的例子
unsigned long mask; unsigned int len = sizeof(mask); if (sched_getaffinity(pid, len, &mask) < 0) { perror("sched_getaffinity"); return -1; } printf("my affinity mask is: %08lx\n", mask);
綁定線程和CPU
與進(jìn)程的情況相似,線程親和性的設(shè)置和獲取主要通過下面兩個(gè)函數(shù)來實(shí)現(xiàn):
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);
從函數(shù)名以及參數(shù)名都很明了,唯一需要點(diǎn)解釋下的可能就是cpu_set_t這個(gè)結(jié)構(gòu)體了。這個(gè)結(jié)構(gòu)體的理解類似于select中的fd_set,可以理解為cpu集,也是通過約定好的宏來進(jìn)行清除、設(shè)置以及判斷:
void CPU_ZERO(cpu_set_t *set); //初始化,設(shè)為空
void CPU_SET(int cpu, cpu_set_t *set); //將某個(gè)cpu加入cpu集中
void CPU_CLR(int cpu, cpu_set_t *set); //將某個(gè)cpu從cpu集中移出
int CPU_ISSET(int cpu, const cpu_set_t *set); //判斷某個(gè)cpu是否已在cpu集中設(shè)置了
進(jìn)程獨(dú)占CPU
如何實(shí)現(xiàn)一個(gè)或多個(gè)進(jìn)程獨(dú)占一個(gè)或多個(gè)CPU? 即調(diào)度器只能把指定的進(jìn)程調(diào)度至指定的CPU。最簡單的方法是利用fork()的繼承特性,子進(jìn)程繼承父進(jìn)程的affinity。這種方法無需修改和編譯內(nèi)核代碼。
init進(jìn)程是所有進(jìn)程的祖先,我們可以設(shè)置init進(jìn)程的affinity來達(dá)到設(shè)置所有進(jìn)程的affinity的目地,然后把我們自己的進(jìn)程綁定到目地CPU上。這樣就到達(dá)了在指定CPU上只運(yùn)行指定的的進(jìn)程的目地。
那么,如何修改init進(jìn)程的affinity?我們只需在/etc/rc.d/rc.sysinit或/etc/rc.sysinit中,起始處增加如下兩行,其中bind是6.1小節(jié)編譯生成的可執(zhí)行文件,rc.sysinit文件是init進(jìn)程運(yùn)行的第一個(gè)腳本。
線程獨(dú)占CPU
通過內(nèi)核參數(shù)isolcpus 來指示系統(tǒng)保留CPU。然后把目標(biāo)線程綁定至保留的CPU。
代碼測試
綁定進(jìn)程
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>
int main(int argc, char *argv[])
{
unsigned long new_mask;
unsigned long cur_mask;
unsigned int len = sizeof(new_mask);
pid_t pid;
if (argc != 3) {
fprintf(stderr,
"usage: %s [pid] [cpu_mask]\n",
argv[0]);
return -1;
}
pid = atol(argv[1]);
sscanf(argv[2], "%08lx", &new_mask);
if (sched_getaffinity(pid, len,
&cur_mask) < 0) {
perror("sched_getaffinity");
return -1;
}
printf("pid %d's old affinity: %08lx\n",
pid, cur_mask);
if (sched_setaffinity(pid, len, &new_mask)) {
perror("sched_setaffinity");
return -1;
}
if (sched_getaffinity(pid, len,
&cur_mask) < 0) {
perror("sched_getaffinity");
return -1;
}
printf(" pid %d's new affinity: %08lx\n",
pid, cur_mask);
return 0;
}
綁定線程
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
void *myfun(void *arg)
{
cpu_set_t mask;
cpu_set_t get;
char buf[256];
int i;
int j;
int num = sysconf(_SC_NPROCESSORS_CONF);
printf("system has %d processor(s)\n", num);
for (i = 0; i < num; i++) {
CPU_ZERO(&mask);
CPU_SET(i, &mask);
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
fprintf(stderr, "set thread affinity failed\n");
}
CPU_ZERO(&get);
if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
fprintf(stderr, "get thread affinity failed\n");
}
for (j = 0; j < num; j++) {
if (CPU_ISSET(j, &get)) {
printf("thread %d is running in processor %d\n", (int)pthread_self(), j);
}
}
j = 0;
while (j++ < 100000000) {
memset(buf, 0, sizeof(buf));
}
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t tid;
if (pthread_create(&tid, NULL, (void *)myfun, NULL) != 0) {
fprintf(stderr, "thread create failed\n");
return -1;
}
pthread_join(tid, NULL);
return 0;
}
參考
[1] http://www.linuxjournal.com/article/6799?page=0,0
[2] http://blog.chinaunix.net/uid-26739406-id-3181199.html