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

參考:http://www.cnblogs.com/wenqiang/p/6049978.htmlhtml

 

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

1、什麼是cpu親和性(affinity)緩存

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

  軟親和性(affinity):  就是進程要在指定的 CPU 上儘可能長時間地運行而不被遷移到其餘處理器,Linux 內核進程調度器天生就具備被稱爲 軟 CPU 親和性(affinity) 的特性,這意味着進程一般不會在處理器之間頻繁遷移。這種狀態正是咱們但願的,由於進程遷移的頻率小就意味着產生的負載小。多線程

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

  解釋:在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用於控制進程能夠在哪裏處理器上運行post

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

2、進程與cpu的綁定性能

   sched_setaffinity能夠將某個進程綁定到一個特定的CPU。你比操做系統更瞭解本身的程序,爲了不調度器愚蠢的調度你的程序,或是爲了在多線程程序中避免緩存失效形成的開銷,你可能會但願這樣作this

  在進行進程與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、一、二、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、一、二、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長整。*/

3、線程與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、一、二、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、一、二、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
相關文章
相關標籤/搜索