导航

多线程cpu affinity问题

Posted on 2014-03-07 21:20  ghostll  阅读(2833)  评论(0编辑  收藏  举报

为了提高多线程程序的性能,有时候需要将线程绑定到固定的cpu core上。

在这个过程中一不小心就会产生编译的问题,但是明明头文件都定义了,却依然编译通不过。

不巧我就遇到了,google也基本搜不到这个问题的解决方案,没办法,只能自己解决了。

下面这个程序就会出现这种问题:

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #define __USE_GNU  
  4. #include <sched.h>  
  5.   
  6. void mybind_cpu(int cpu_id)  
  7. {  
  8.     cpu_set_t mask;  
  9.     //! sched_setaffinity  
  10.     CPU_ZERO(&mask);  
  11.     CPU_SET(cpu_id, &mask);  
  12.     if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) < 0) {  
  13.         printf("Error: cpu id %d sched_setaffinity\n", cpu_id);  
  14.         printf("Warning: performance may be impacted \n");  
  15.     }  
  16.     return;  
  17. }  
  18.   
  19. void test_thread(void *cpu_id)  
  20. {  
  21.     int cpuid = (int)(long)cpu_id;    
  22.     mybind_cpu(cpuid);  
  23.     printf("test success!\n");  
  24.     return;  
  25. }  
  26.   
  27. int main(int argc, char **argv)  
  28. {  
  29.     int res;  
  30.     void *thread_result;  
  31.     pthread_t test_thread_ctrl;  
  32.     pthread_create(&test_thread_ctrl, NULL, (void *)test_thread, (void *)0);  
  33.     res = pthread_join(test_thread_ctrl, &thread_result);  
  34.     (void)res;  
  35.     return 0;  
  36. }  


运行编译命令,编译无法通过:

 

gcc  test.c -g -Wall -lpthread -o test 
test.c: In function ‘mybind_cpu’:
test.c:10:5: warning: implicit declaration of function ‘CPU_ZERO’ [-Wimplicit-function-declaration]
     CPU_ZERO(&mask);
     ^
test.c:11:5: warning: implicit declaration of function ‘CPU_SET’ [-Wimplicit-function-declaration]
     CPU_SET(cpu_id, &mask);
     ^
test.c:12:5: warning: implicit declaration of function ‘sched_setaffinity’ [-Wimplicit-function-declaration]
     if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) < 0) {
     ^
/tmp/cc0z5zPj.o: In function `mybind_cpu':
/home/youfu/test.c:10: undefined reference to `CPU_ZERO'
/home/youfu/test.c:11: undefined reference to `CPU_SET'
collect2: error: ld returned 1 exit status

可以看到提示一些变量没有定义, 但是我们明明include了头文件<sched.h>,而且前面添加了#define __USE_GNU,莫非这些宏定义是在别的头文件中吗?

我们打开/usr/include/sched.h文件, 发现只要#define __USE_GNU,那么就会定义相关的宏,sched.h文件相关定义的源码如下:

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #ifdef __USE_GNU  
  2. /* Access macros for `cpu_set'.  */  
  3. # define CPU_SETSIZE __CPU_SETSIZE  
  4. # define CPU_SET(cpu, cpusetp)   __CPU_SET_S (cpu, sizeof (cpu_set_t), cpusetp)  
  5. # define CPU_CLR(cpu, cpusetp)   __CPU_CLR_S (cpu, sizeof (cpu_set_t), cpusetp)  
  6. # define CPU_ISSET(cpu, cpusetp) __CPU_ISSET_S (cpu, sizeof (cpu_set_t), \  
  7.                         cpusetp)  
  8. # define CPU_ZERO(cpusetp)   __CPU_ZERO_S (sizeof (cpu_set_t), cpusetp)  
  9. # define CPU_COUNT(cpusetp)  __CPU_COUNT_S (sizeof (cpu_set_t), cpusetp)  
  10.   
  11. # define CPU_SET_S(cpu, setsize, cpusetp)   __CPU_SET_S (cpu, setsize, cpusetp)  
  12. # define CPU_CLR_S(cpu, setsize, cpusetp)   __CPU_CLR_S (cpu, setsize, cpusetp)  
  13. # define CPU_ISSET_S(cpu, setsize, cpusetp) __CPU_ISSET_S (cpu, setsize, \  
  14.                                cpusetp)  
  15. # define CPU_ZERO_S(setsize, cpusetp)       __CPU_ZERO_S (setsize, cpusetp)  
  16. # define CPU_COUNT_S(setsize, cpusetp)      __CPU_COUNT_S (setsize, cpusetp)  
  17.   
  18. # define CPU_EQUAL(cpusetp1, cpusetp2) \  
  19.   __CPU_EQUAL_S (sizeof (cpu_set_t), cpusetp1, cpusetp2)  
  20. # define CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \  
  21.   __CPU_EQUAL_S (setsize, cpusetp1, cpusetp2)  
  22.   
  23. # define CPU_AND(destset, srcset1, srcset2) \  
  24.   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, &)  
  25. # define CPU_OR(destset, srcset1, srcset2) \  
  26.   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, |)  
  27. # define CPU_XOR(destset, srcset1, srcset2) \  
  28.   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, ^)  
  29. # define CPU_AND_S(setsize, destset, srcset1, srcset2) \  
  30.   __CPU_OP_S (setsize, destset, srcset1, srcset2, &)  
  31. # define CPU_OR_S(setsize, destset, srcset1, srcset2) \  
  32.   __CPU_OP_S (setsize, destset, srcset1, srcset2, |)  
  33. # define CPU_XOR_S(setsize, destset, srcset1, srcset2) \  
  34.   __CPU_OP_S (setsize, destset, srcset1, srcset2, ^)  
  35.   
  36. # define CPU_ALLOC_SIZE(count) __CPU_ALLOC_SIZE (count)  
  37. # define CPU_ALLOC(count) __CPU_ALLOC (count)  
  38. # define CPU_FREE(cpuset) __CPU_FREE (cpuset)  
  39.   
  40.   
  41. /* Set the CPU affinity for a task */  
  42. extern int sched_setaffinity (__pid_t __pid, size_t __cpusetsize,  
  43.                   const cpu_set_t *__cpuset) __THROW;  
  44.   
  45. /* Get the CPU affinity for a task */  
  46. extern int sched_getaffinity (__pid_t __pid, size_t __cpusetsize,  
  47.                   cpu_set_t *__cpuset) __THROW;  
  48. #endif  


好的,没办法,通过gcc的预编译选项来查看到底预编译的代码是什么, 在预处理生成的文件中有没有相关定义,运行下面命令生成预处理文件test.i

 

gcc -E test.c -g -Wall -lpthread -o test.i

预编译生成的文件非常大,大概有2000多行,找到其中sched.h相关的部分,发现预处理没有把#define __USE_GNU相关的宏定义包含进来:

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. extern int sched_setparam (__pid_t __pid, const struct sched_param *__param)  
  2.      __attribute__ ((__nothrow__ , __leaf__));  
  3.   
  4.   
  5. extern int sched_getparam (__pid_t __pid, struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__));  
  6.   
  7.   
  8. extern int sched_setscheduler (__pid_t __pid, int __policy,  
  9.           const struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__));  
  10.   
  11.   
  12. extern int sched_getscheduler (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__));  
  13.   
  14.   
  15. extern int sched_yield (void) __attribute__ ((__nothrow__ , __leaf__));  
  16.   
  17.   
  18. extern int sched_get_priority_max (int __algorithm) __attribute__ ((__nothrow__ , __leaf__));  
  19.   
  20.   
  21. extern int sched_get_priority_min (int __algorithm) __attribute__ ((__nothrow__ , __leaf__));  
  22.   
  23.   
  24. extern int sched_rr_get_interval (__pid_t __pid, struct timespec *__t) __attribute__ ((__nothrow__ , __leaf__));  
  25. # 125 "/usr/include/sched.h" 3 4  


第一感觉这个肯定不是gcc的bug, 这就说明__USE_GNU这个宏定义不知到为什么没有起作用。

 

只能是pthread.h在作怪,打开/usr/include/pthread.h, 重大发现:

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <features.h>  
  2. #include <endian.h>  
  3. #include <sched.h>  
  4. #include <time.h>  
  5.   
  6. #include <bits/pthreadtypes.h>  
  7. #include <bits/setjmp.h>  
  8. #include <bits/wordsize.h>  


在pthread.h中也include 了<sched.h>,但是没有定义__USE_GNU,所以后面的include <sched.h>就没有作用了。

 

其实从预编译生成的文件就可以看出来, pthread相关的定义是在最后,但是明显我们是pthread.h是在sched.h前面的,这个也可以说明我们自己的c文件中的sched.h没有被包含进来,而是包含别的头文件中的sched.h

解决方案就很简单了,将pthread.h的定义放到sched.h的后面就可以了。

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <stdio.h>  
  2. #define __USE_GNU  
  3. #include <sched.h>  
  4. #include <pthread.h>  



 

gcc  test.c -g -Wall -lpthread -o test 
 ./test 

test success!

 

打完收工!

我的专栏