Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1020986
  • 博文数量: 326
  • 博客积分: 10135
  • 博客等级: 上将
  • 技术积分: 2490
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-22 23:53
文章分类

全部博文(326)

文章存档

2014年(1)

2012年(4)

2011年(1)

2010年(4)

2009年(41)

2008年(44)

2007年(63)

2006年(168)

我的朋友

分类:

2011-12-14 11:00:04

原文地址:进程和线程的CPU亲和性 作者:qqrilxk

进程和线程的亲缘性(affinity)是指可以将进程或者是线程强制限制在可用的CPU子集上运行的特性,它一定程度上把进程/线程在多处理器系统上的调度策略暴露给系统程序员。
CPU的数量和表示在有n个CPU的Linux上,CPU是用0...n-1来进行一一标识的。CPU的数量可以通过proc文件系统下的CPU相关文件得到,如cpuinfo和stat:
$ cat /proc/stat | grep "^cpu[0-9]\+" | wc -l
8
$ cat /proc/cpuinfo | grep "^processor" | wc -l
8
在系统编程中,可以直接调用库调用sysconf获得:
sysconf(_SC_NPROCESSORS_ONLN);
进程的亲缘性Linux操作系统在2.5.8引入了调度亲缘性相关的系统调用:
int
sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
int
sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
其中sched_setaffinity是设定进程号为pid的进程调度亲缘性为mask,也就是说它只能在mask中指定的CPU之间进行调度执行;sched_getaffinity当然就是得到进程号为pid的进程调度亲缘性了。如果pid为0,则操纵当前进程。
第二个参数指定mask所指空间的大小,通常为sizeof(cpu_set_t)。
第三个参数mask的类型为cpu_set_t,即CPU集合,GNU的c库(需要在include头文件之前定义__USE_GNU)还提供了操作它们的宏:
void
CPU_CLR(int cpu, cpu_set_t *set);
int
CPU_ISSET(int cpu, cpu_set_t *set);
void
CPU_SET(int cpu, cpu_set_t *set);
void
CPU_ZERO(cpu_set_t *set);
 

如果我们所关心的只是CPU#0和CPU#1,想确保我们的进程只会运作在CPU#0之上,而不会运作在CPU#1之上。下面程序代码可以完成此事:

  1. cpu_set_t set;  
  2. int ret, i;  
  3.   
  4. CPU_ZERO(&set);  
  5. CPU_SET(0, &set);  
  6. CPU_CLR(1, &set);  
  7.   
  8. ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);  
  9. if( ret == -1)  
  10. {  
  11.         perror("sched_se");  
  12. }  
  13.   
  14. for( i=0; i < 3; i++)  
  15. {  
  16.         int cpu;  
  17.         cpu = CPU_ISSET(i, &set);  
  18.         printf("cpu = %i is %s/n", i,  
  19.                 cpu? "set" : "unset");  
  20. }  
 

Linux只提供了面向线程的调度亲缘性一种接口,这也是上面只提调度亲缘性而不直言进程亲缘性的原因。当前Linux系统下广泛采用的线程库NPTL(Native Posix Thread Library)是基于线程组来实现的,同一个线程组中的线程对应于一组共享存储空间的轻量级进程,它们各自作为单独调度单位被内核的调度器在系统范围内调度,这种模型也就是我们通常所说的1-1线程模型。正因如此,目前线程的调度范围(可以用函数pthread_attr_getscope和pthread_attr_setscope获取和设置)只能是系统级而不能是进程级。
 
c库的GNU扩展所提供的有关线程亲缘性的API如下:
int pthread_attr_setaffinity_np (pthread_attr_t *__attr, size_t __cpusetsize, __const cpu_set_t *__cpuset);
int
pthread_attr_getaffinity_np (__const pthread_attr_t *__attr, size_t __cpusetsize, cpu_set_t *__cpuset);
int pthread_setaffinity_np (pthread_t __th, size_t __cpusetsize, __const cpu_set_t *__cpuset);
int
pthread_getaffinity_np (pthread_t __th, size_t __cpusetsize, cpu_set_t *__cpuset);
亲缘性的继承调度亲缘性是被fork出来的子进程所继承的,即使子进程通过exec系列函数更换了执行镜像。因为Linux操作系统下进程和线程的创建都是通过系统调用clone来实现的,所以实际上调度亲缘性也是被用pthread_create创建的线程所继承的。这意味着,如果主线程在创建其它线程之前设定亲缘性,那么它所设定的亲缘性将被继承,因为这时所有线程的亲缘性相同(假设之后没有任何线程私自设置亲缘性),我们就可以认为前面设置的是进程亲缘性,而不管它所调用的函数是sched_setaffinity还是pthread_setaffnity_np。
 
下面创建两个并发线程分别绑定在CPU0和CPU1上。
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int x1;
int x2;
double waste_time(long n)
{
        double res = 0;
        long i = 0;
        while (i                 i++;
                res += sqrt(i);
        }
        return res;
}

void* proc1(void*arg)
{
    cpu_set_t mask ;
    CPU_ZERO(&mask);
    CPU_SET(0,&mask);
    int ret = 0;
   
     ret = pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
     if(ret < 0)
    {
        printf("pthread_setaffinity_np err \n");
        return ;
    }
    while(1)
    {
        if(x1 > 900000000)
        {
            break;
        }
        x1++;
    }
    waste_time(1);
    ret =pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
    if(ret < 0)
    {
        printf("pthread_getaffinity_np err \n");
        return ;
    }
    int j;
    for( j = 0;j < CPU_SETSIZE;j++)
    {
        if(CPU_ISSET(j,&mask))
            printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);
    }
}
void* proc2(void* arg)
{
    cpu_set_t mask ;
    CPU_ZERO(&mask);
    CPU_SET(2,&mask);
    int ret = 0;
   
    ret =pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
    if(ret < 0)
    {
        printf("pthread_setaffinity_np err \n");
        return ;
    }

    while(1)
    {
        if(x2 > 900000000)
        {
            break;
        }
        x2++;
    }
    waste_time(1);
    ret =pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
    if(ret < 0)
    {
        printf("pthread_getaffinity_np err \n");
        return ;
    }
    int j;
    for( j = 0;j < CPU_SETSIZE;j++)
    {
        if(CPU_ISSET(j,&mask))
            printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);
    }
}
void main()
{   
    int ret;
    pthread_t t1,t2;
    struct timeval time1,time2;
   
    ret = gettimeofday(&time1,NULL);
    ret = pthread_create(&t1,NULL,proc1,NULL);   
    ret = pthread_create(&t2,NULL,proc2,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    ret = gettimeofday(&time2,NULL);
    printf("time spend:[%d]s [%d]ms \n",time2.tv_sec - time1.tv_sec,(time2.tv_usec - time1.tv_usec)/1000);
}

 
阅读(1733) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~