linux下把程式繫結到特定cpu核上執行

大雄45發表於2021-10-25
導讀 現在大家使用的基本上都是多核cpu,一般是4核的。平時應用程式在執行時都是由作業系統管理的。作業系統對應用程式進行排程,使其在不同的核上輪番執行。

對於普通的應用,作業系統的預設排程機制是沒有問題的。但是,當某個程式需要較高的執行效率時,就有必要考慮將其繫結到單獨的核上執行,以減小由於在不同的核上排程造成的開銷。

把某個程式/執行緒繫結到特定的cpu核上後,該程式就會一直在此核上執行,不會再被作業系統排程到其他核上。但繫結的這個核上還是可能會被排程執行其他應用程式的。

作業系統對多核cpu的排程

目前windows和 都支援對多核cpu進行排程管理。

軟體開發在多核環境下的核心是多執行緒開發。這個多執行緒不僅代表了軟體實現上多執行緒,要求在硬體上也採用多執行緒技術。

多核作業系統的關注點在於程式的分配和排程。程式的分配將程式分配到合理的物理核上,因為不同的核在共享性和歷史執行情況都是不同的。有的物理核能夠共享二級cache,而有的卻是獨立的。如果將有資料共享的程式分配給有共享二級cache的核上,將大大提升效能;反之,就有可能影響效能。

程式排程會涉及實時性、負載均衡等問題,目前研究的熱點問題主要集中在以下方面:

  1. 程式的並行開發設計
  2. 多程式的時間相關性
  3. 任務的分配和排程
  4. 快取的錯誤共享
  5. 一致性訪問問題
  6. 程式間通訊
  7. 多處理器核內部資源競爭

多程式和多執行緒在cpu核上執行時情況如下:
每個 CPU 核執行一個程式的時候,由於每個程式的資源都獨立,所以 CPU 核心之間切換的時候無需考慮上下文
每個 CPU 核執行一個執行緒的時候,有時執行緒之間需要共享資源,所以這些資源必須從 CPU 的一個核心被複制到另外一個核心,這會造成額外的開銷

繫結程式到cpu核上執行
檢視cpu有幾個核

使用 cat /proc/cpuinfo檢視cpu資訊,如下兩個資訊:

processor,指明第幾個cpu處理器
cpu cores,指明每個處理器的核心數
也可以使用系統呼叫sysconf獲取cpu核心數:

#include <unistd.h>
int sysconf(_SC_NPROCESSORS_CONF);/* 返回系統可以使用的核數,但是其值會包括系統中禁用的核的數目,因 此該值並不代表當前系統中可用的核數 */
int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系統當前可用的核數 */
/* 以下兩個函式與上述類似 */
#include <sys/sysinfo.h>
int get_nprocs_conf (void);/* 可用核數 */
int get_nprocs (void);/* 真正的反映了當前可用核數 */

我使用的是虛擬機器,有2個處理器,每個處理器只有一個核,等同於一個處理器兩個核心。

使用taskset指令

獲取程式pid

-> % ps
PID TTY TIME CMD
2683 pts/1 00:00:00 zsh
2726 pts/1 00:00:00 dgram_servr
2930 pts/1 00:00:00 ps

檢視程式當前執行在哪個cpu上

-> % taskset -p 2726
pid 2726's current affinity mask: 3

顯示的十進位制數字3轉換為2進製為最低兩個是1,每個1對應一個cpu,所以程式執行在2個cpu上。

指定程式執行在cpu1上

-> % taskset -pc 1 2726
pid 2726's current affinity list: 0,1
pid 2726's new affinity list: 1

注意,cpu的標號是從0開始的,所以cpu1表示第二個cpu(第一個cpu的標號是0)。

至此,就把應用程式繫結到了cpu1上執行,檢視如下:

-> % taskset -p 2726
pid 2726's current affinity mask: 2

啟動程式時繫結cpu

#啟動時繫結到第二個cpu
-> % taskset -c 1 ./dgram_servr&
[1] 3011
#檢視確認繫結情況
-> % taskset -p 3011
pid 3011's current affinity mask: 2
使用sched_setaffinity系統呼叫

sched_setaffinity可以將某個程式繫結到一個特定的CPU。

#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sched.h>
/* 設定程式號為pid的程式執行在mask所設定的CPU上
* 第二個引數cpusetsize是mask所指定的數的長度
* 通常設定為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所指向的結構中 */

例項

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/sysinfo.h>
#include<unistd.h>
#define __USE_GNU
#include<sched.h>
#include<ctype.h>
#include<string.h>
#include<pthread.h>
#define THREAD_MAX_NUM 200 //1個CPU內的最多程式數
int num=0; //cpu中核數
void* threadFun(void* arg) //arg 傳遞執行緒標號(自己定義)
{
cpu_set_t mask; //CPU核的集合
cpu_set_t get; //獲取在集合中的CPU
int *a = (int *)arg;
int i;
printf("the thread is:%d\n",*a); //顯示是第幾個執行緒
CPU_ZERO(&mask); //置空
CPU_SET(*a,&mask); //設定親和力值
if (sched_setaffinity(0, sizeof(mask), &mask) == -1)//設定執行緒CPU親和力
{
printf("warning: could not set CPU affinity, continuing...\n");
}
CPU_ZERO(&get);
if (sched_getaffinity(0, sizeof(get), &get) == -1)//獲取執行緒CPU親和力
{
printf("warning: cound not get thread affinity, continuing...\n");
}
for (i = 0; i < num; i++)
{
if (CPU_ISSET(i, &get))//判斷執行緒與哪個CPU有親和力
{
printf("this thread %d is running processor : %d\n", i,i);
}
}
return NULL;
}
int main(int argc, char* argv[])
{
int tid[THREAD_MAX_NUM];
int i;
pthread_t thread[THREAD_MAX_NUM];
num = sysconf(_SC_NPROCESSORS_CONF); //獲取核數
if (num > THREAD_MAX_NUM) {
printf("num of cores[%d] is bigger than THREAD_MAX_NUM[%d]!\n", num, THREAD_MAX_NUM);
return -1;
}
printf("system has %i processor(s). \n", num);
for(i=0;i<num;i++)
{
tid[i] = i; //每個執行緒必須有個tid[i]
pthread_create(&thread[i],NULL,threadFun,(void*)&tid[i]);
}
for(i=0; i< num; i++)
{
pthread_join(thread[i],NULL);//等待所有的執行緒結束,執行緒為死迴圈所以CTRL+C結束
}
return 0;
}

執行結果

-> % ./a.out
system has 2 processor(s).
the thread is:0
the thread is:1
this thread 0 is running processor : 0
this thread 1 is running processor : 1
繫結執行緒到cpu核上執行

繫結執行緒到cpu核上使用pthread_setaffinity_np函式,其原型定義如下:

#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <pthread.h>
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);
Compile and link with -pthread.

各引數的意義與sched_setaffinity相似。

例項

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
int s, j;
cpu_set_t cpuset;
pthread_t thread;
thread = pthread_self();
/* Set affinity mask to include CPUs 0 to 7 */
CPU_ZERO(&cpuset);
for (j = 0; j < 8; j++)
CPU_SET(j, &cpuset);
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
if (s != 0)
handle_error_en(s, "pthread_setaffinity_np");
/* Check the actual affinity mask assigned to the thread */
s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
if (s != 0)
handle_error_en(s, "pthread_getaffinity_np");
printf("Set returned by pthread_getaffinity_np() contained:\n");
for (j = 0; j < CPU_SETSIZE; j++)
if (CPU_ISSET(j, &cpuset))
printf(" CPU %d\n", j);
exit(EXIT_SUCCESS);
}

執行結果

-> % ./a.out
Set returned by pthread_getaffinity_np() contained:
CPU 0
CPU 1
總結

可以使用多種方法把程式/執行緒指定到特定的cpu核上執行。

本文原創地址:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2839059/,如需轉載,請註明出處,否則將追究法律責任。

相關文章