linux進程、線程與cpu的親和性(affinity)


最近的工作中對性能的要求比較高,下面簡單做一下總結:

一、什么是cpu親和性(affinity)

  CPU的親和性, 就是進程要在指定的 CPU 上盡量長時間地運行而不被遷移到其他處理器,也稱為CPU關聯性;再簡單的點的描述就將制定的進程或線程綁定到相應的cpu上;在多核運行的機器上,每個CPU本身自己會有緩存,緩存着進程使用的信息,而進程可能會被OS調度到其他CPU上,如此,CPU cache命中率就低了,當綁定CPU后,程序就會一直在指定的cpu跑,不會由操作系統調度到其他CPU上,性能有一定的提高。

  軟親和性(affinity):  就是進程要在指定的 CPU 上盡量長時間地運行而不被遷移到其他處理器,Linux 內核進程調度器天生就具有被稱為 軟 CPU 親和性(affinity) 的特性,這意味着進程通常不會在處理器之間頻繁遷移。這種狀態正是我們希望的,因為進程遷移的頻率小就意味着產生的負載小。

  硬親和性(affinity):簡單來說就是利用linux內核提供給用戶的API,強行將進程或者線程綁定到某一個指定的cpu核運行。

  解釋:在linux內核中,所有的進程都有一個相關的數據結構,稱為 task_struct。這個結構非常重要,原因有很多;其中與 親和性(affinity)相關度最高的是 cpus_allowed 位掩碼。這個位掩碼由 n 位組成,與系統中的 n 個邏輯處理器一一對應。 具有 4 個物理 CPU 的系統可以有 4 位。如果這些 CPU 都啟用了超線程,那么這個系統就有一個 8 位的位掩碼。 如果為給定的進程設置了給定的位,那么這個進程就可以在相關的 CPU 上運行。因此,如果一個進程可以在任何 CPU 上運行,並且能夠根據需要在處理器之間進行遷移,那么位掩碼就全是 1。實際上,這就是 Linux 中進程的缺省狀態;(這部分內容在這個博客中有提到一點:http://www.cnblogs.com/wenqiang/p/4802619.html

   cpus_allowed用於控制進程可以在哪里處理器上運行

  • sched_set_affinity() (用來修改位掩碼)
  • sched_get_affinity() (用來查看當前的位掩碼)

二、進程與cpu的綁定

   sched_setaffinity可以將某個進程綁定到一個特定的CPU。你比操作系統更了解自己的程序,為了避免調度器愚蠢的調度你的程序,或是為了在多線程程序中避免緩存失效造成的開銷,你可能會希望這樣做

  在進行進程與cpu的綁定前,我們先了解編寫程序需要准備的知識點

 1 SCHED_SETAFFINITY(2)                                                                      Linux Programmer's Manual                                                                     SCHED_SETAFFINITY(2)
 2 
 3 NAME
 4        sched_setaffinity, sched_getaffinity - set and get a process's CPU affinity mask
 5 
 6 SYNOPSIS
 7        #define _GNU_SOURCE             /* See feature_test_macros(7) */
 8        #include <sched.h>
 9 
10        int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
11        /*該函數設置進程為pid的這個進程,讓它運行在mask所設定的CPU上.如果pid的值為0,
12         *則表示指定的是當前進程,使當前進程運行在mask所設定的那些CPU上.
13         *第二個參數cpusetsize是mask所指定的數的長度.通常設定為sizeof(cpu_set_t).
14         *如果當前pid所指定的進程此時沒有運行在mask所指定的任意一個CPU上,
15         *則該指定的進程會從其它CPU上遷移到mask的指定的一個CPU上運行.*/
16 
17        int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
18        /*該函數獲得pid所指示的進程的CPU位掩碼,並將該掩碼返回到mask所指向的結構中.
19         *即獲得指定pid當前可以運行在哪些CPU上.
20         *同樣,如果pid的值為0.也表示的是當前進程*/
21 
22 RETURN VALUE
23        On success, sched_setaffinity() and sched_getaffinity() return 0.  On error, -1 is returned, and errno is set appropriately.

 設置cpu affinity還需要用到一下宏函數

1 void CPU_ZERO (cpu_set_t *set)
2 /*這個宏對 CPU 集 set 進行初始化,將其設置為空集。*/
3 void CPU_SET (int cpu, cpu_set_t *set)
4 /*這個宏將 指定的 cpu 加入 CPU 集 set 中*/
5 void CPU_CLR (int cpu, cpu_set_t *set)
6 /*這個宏將 指定的 cpu 從 CPU 集 set 中刪除。*/
7 int CPU_ISSET (int cpu, const cpu_set_t *set)
8 /*如果 cpu 是 CPU 集 set 的一員,這個宏就返回一個非零值(true),否則就返回零(false)。*/

下面下一個具體的例子:將當前進程綁定到0、1、2、3號cpu上

 1 #define _GNU_SOURCE
 2 #include <sched.h>
 3 #include <stdio.h>
 4 #include <string.h>
 5 #include <stdlib.h>
 6 #include <unistd.h>
 7 #include <errno.h>
 8 
 9 /* sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的個數;打印用%ld長整。
10  * sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu個數;打印用%ld長整 */
11 int main(int argc, char **argv)
12 {
13     int cpus = 0;
14     int  i = 0;
15     cpu_set_t mask;
16     cpu_set_t get;
17 
18     cpus = sysconf(_SC_NPROCESSORS_CONF);
19     printf("cpus: %d\n", cpus);
20 
21     CPU_ZERO(&mask);    /* 初始化set集,將set置為空*/
22     CPU_SET(0, &mask);  /* 依次將0、1、2、3號cpu加入到集合,前提是你的機器是多核處理器*/
23     CPU_SET(1, &mask);
24     CPU_SET(2, &mask);
25     CPU_SET(3, &mask);
26     
27     /*設置cpu 親和性(affinity)*/
28     if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
29         printf("Set CPU affinity failue, ERROR:%s\n", strerror(errno));
30         return -1; 
31     }   
32     usleep(1000); /* 讓當前的設置有足夠時間生效*/
33 
34     /*查看當前進程的cpu 親和性*/
35     CPU_ZERO(&get);
36     if (sched_getaffinity(0, sizeof(get), &get) == -1) {
37         printf("get CPU affinity failue, ERROR:%s\n", strerror(errno));
38         return -1; 
39     }   
40     
41     /*查看運行在當前進程的cpu*/
42     for(i = 0; i < cpus; i++) {
43 
44         if (CPU_ISSET(i, &get)) { /*查看cpu i 是否在get 集合當中*/
45             printf("this process %d of running processor: %d\n", getpid(), i); 
46         }    
47     }
48     sleep(3); //讓程序停在這兒,方便top命令查看
49        
50     return 0;
51 }

運行結果如下:

1 [root@localhost test]# ./test    
2 cpus: 24
3 this process 2848 of running processor: 0
4 this process 2848 of running processor: 1
5 this process 2848 of running processor: 2
6 this process 2848 of running processor: 3

上面代碼當中用到了syscall這個函數,順便也在這里做一下說明

  syscall是執行一個系統調用,根據指定的參數number和所有系統調用的接口來確定調用哪個系統調用,用於用戶空間跟內核之間的數據交換

  下面是syscall函數原型及一些常用的number 

 1 //syscall - indirect system call
 2 SYNOPSIS
 3        #define _GNU_SOURCE         /* See feature_test_macros(7) */
 4        #include <unistd.h>
 5        #include <sys/syscall.h>   /* For SYS_xxx definitions */
 6 
 7        int syscall(int number, ...);
 8        
 9 /* sysconf( _SC_PAGESIZE );  此宏查看緩存內存頁面的大小;打印用%ld長整型。
10  sysconf( _SC_PHYS_PAGES ) 此宏查看內存的總頁數;打印用%ld長整型。
11  sysconf( _SC_AVPHYS_PAGES ) 此宏查看可以利用的總頁數;打印用%ld長整型。
12  sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的個數;打印用%ld長整。
13  sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu個數;打印用%ld長整。
14  (long long)sysconf(_SC_PAGESIZE) * (long long)sysconf(_SC_PHYS_PAGES) 計算內存大小。
15  sysconf( _SC_LOGIN_NAME_MAX ) 查看最大登錄名長度;打印用%ld長整。
16  sysconf( _SC_HOST_NAME_MAX ) 查看最大主機長度;打印用%ld長整。
17  sysconf( _SC_OPEN_MAX )  每個進程運行時打開的文件數目;打印用%ld長整。
18  sysconf(_SC_CLK_TCK) 查看每秒中跑過的運算速率;打印用%ld長整。*/

三、線程與cpu的綁定

線程於進程的綁定方法大體一致,需要注意的是線程綁定於進程的區別是所用函數不一樣

線程綁定用到下面兩個函數,跟進程類似就不做詳細說明,下面直接貼出函數原型:

 1 NAME
 2        pthread_setaffinity_np, pthread_getaffinity_np - set/get CPU affinity of a thread
 3 
 4 SYNOPSIS
 5        #define _GNU_SOURCE             /* See feature_test_macros(7) */
 6        #include <pthread.h>
 7 
 8        int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
 9                                   const cpu_set_t *cpuset);
10        int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
11                                   cpu_set_t *cpuset);
12 
13        Compile and link with -pthread.
14 
15 DESCRIPTION
16        The  pthread_setaffinity_np()  function  sets the CPU affinity mask of the thread thread to the CPU set pointed to by cpuset.  If the call is successful, and the thread is not
17        currently running on one of the CPUs in cpuset, then it is migrated to one of those CPUs.
18 
19        The pthread_getaffinity_np() function returns the CPU affinity mask of the thread thread in the buffer pointed to by cpuset.
20 
21        For more details on CPU affinity masks, see sched_setaffinity(2).  For a description of a set of macros that can be used to manipulate and inspect CPU sets, see CPU_SET(3).
22 
23        The argument cpusetsize is the length (in bytes) of the buffer pointed to by cpuset.  Typically, this argument would be specified as sizeof(cpu_set_t).  (It may be some  other
24        value, if using the macros described in CPU_SET(3) for dynamically allocating a CPU set.)
25 
26 RETURN VALUE
27        On success, these functions return 0; on error, they return a nonzero error number

 

下面同樣是個具體的例子:將當前線程綁定到0、1、2、3號cpu上

 1 #define _GNU_SOURCE
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 #include <pthread.h>
 7 #include <sched.h>
 8 
 9 void *testfunc(void *arg)
10 {
11     int i, cpus = 0;
12     cpu_set_t mask;
13     cpu_set_t get;
14 
15     cpus = sysconf(_SC_NPROCESSORS_CONF);
16     printf("this system has %d processor(s)\n", cpus);
17     
18     CPU_ZERO(&mask);
19     for (i = 0; i < 4; i++) { /*將0、1、2、3添加到集合中*/
20         CPU_SET(i, &mask);
21     }   
22 
23     /* 設置cpu 親和性(affinity)*/
24     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
25         fprintf(stderr, "set thread affinity failed\n");
26     }   
27     
28     /* 查看cpu 親和性(affinity)*/
29     CPU_ZERO(&get);
30     if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
31         fprintf(stderr, "get thread affinity failed\n");
32     }   
33 
34     /* 查看當前線程所運行的所有cpu*/
35     for (i = 0; i < cpus; i++) {
36         if (CPU_ISSET(i, &get)) {
37             printf("this thread %d is running in processor %d\n", (int)pthread_self(), i); 
38         }   
39     }   
40     sleep(3); //查看
41     
42     pthread_exit(NULL);
43 }
44  
45 int main(int argc, char *argv[])
46 {
47     pthread_t tid;
48     if (pthread_create(&tid, NULL, (void *)testfunc, NULL) != 0) {
49         fprintf(stderr, "thread create failed\n");
50         return -1; 
51     }   
52 
53     pthread_join(tid, NULL);
54     return 0;
55 }

運行結果如下:

1 [root@localhost thread]# ./test                      
2 this system has 24 processor(s)
3 this thread 2812323584 is running in processor 0
4 this thread 2812323584 is running in processor 1
5 this thread 2812323584 is running in processor 2
6 this thread 2812323584 is running in processor 3

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM