曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 13203|回复: 1
打印 上一主题 下一主题

CPU 亲和性

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2010-9-12 21:54:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
CPU_ZERO

什么是 CPU 的亲和性?
CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。

CPU 的亲和性常用在多 CPU 或 多核的编程中。

在多核编程里,有一个基本数据类型:
cpu_set_t
它表示一个 CPU 集的掩码。定义是这样的:
/* Data structure to describe CPU mask.  */
typedef struct
{
   __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;
__cpu_mask 的定义为:
/* Type for array elements in 'cpu_set_t'.  */
typedef unsigned long int __cpu_mask;
所以 cpu_set_t 是个无符号长整型数组。
__CPU_SETSIZE 和 __NCPUBITS 在 32 位机上的定义为:
# define __CPU_SETSIZE  1024
# define __NCPUBITS     (8 * sizeof (__cpu_mask))
所以,最后知道 cpu_set_t 在 32 位机上实际上是一个有 32 个无符号长整型元素的数组。

CPU_ZERO 宏用来将 cpu_set_t 类型变量进行清零初始化。初始化清零,也就意味着此时不管哪个 CPU 都没有对进程/线程 具有亲和性。它的定义如下:
# define CPU_ZERO(cpusetp)       __CPU_ZERO_S (sizeof (cpu_set_t), cpusetp)
上面,cpusetp 是 cpu_set_t 类型的指针。sizeof(cpu_set_t) 在 32 位机里的大小为 128 个字节,即 32 x 4 = 128,其中一个长整型的大小为 4 个字节。
__CPU_ZERO_S 定义如下:
#  define __CPU_ZERO_S(setsize, cpusetp) \
do __builtin_memset (cpusetp, '\', setsize); while (0)
由上可见,这是对 32 个长整型数组空间进行清零。注意,cpu_set_t 最终还是定义为结构体类型,因为结构体类型更具有可移植性,在这里也可以看到有利于利用 memset 进行初始化数组空间。

那么,我们在 32 位机上到底是可以管理 32 个 CPU 还是 128 个还是 128 * 8 = 1024 个?从下面对 CPU_SET 宏的分析,我们可以知道答案是 1024 个!

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2010-9-12 21:55:15 | 只看该作者

CPU 亲和性. CPU_SET

CPU_SET 的也是个函数宏定义,它的原型是:
void CPU_SET(int cpu, cpu_set_t *set);

查看 seched.h 里的具体定义:
#define CPU_SET(cpu, cpusetp)   __CPU_SET_S (cpu, sizeof (cpu_set_t), cpusetp)
在 CPU_SET() 里,第 1 个参数表示第几个 cpu ,第 2 个参数是一个 cpu_set_t 类型指针。
在 __CPU_SET_S 里,第 2 个参数在 32 位机里,它为 128 。

下面进一步看 __CPU_SET_S 的定义:
# define __CPU_SET_S(cpu, setsize, cpusetp) \\
(__extension__                                                              \\
({ size_t __cpu = (cpu);                                                   \\
__cpu < 8 * (setsize)                                                   \\
? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)]               \\
|= __CPUMASK (__cpu))                                                \\
: 0; }))
上面,setsize * 8 = 1024 ,也就是一个可以有 1024 个位来设置 1024 个 CPU 的掩码。宏中的第 2 条语句的定义的意思是,如果输入的 cpu 的值大于 1024,那么就设置第 1 个 CPU 的掩码(第 0 位)。

上面说过,cpu_set_t 实际上是一个只包含整型数组的结构体。所以,在 cpusetp->__bits (__bit 是数组名)前面加上 (__cpu_mask *) 进行类型转换(__cpu_mask 也是无符号整型),说明这是一个无符号整型数组。

再看数组元素的下标的定义:
# define __CPUELT(cpu)  ((cpu) / __NCPUBITS)
从上面知道,__NCPUBITS 是 32 ,刚好是 4 个字节,为一个整型大小(实际上也就是无符号整型数组中的元素大小)。

这里假设输入参数 cpu 是 1,,那么 cpu / __NCPUBITS 为 0. 从((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)]  知道,我们要设定 CPU 是在数组中的第一个元素里。__CPUMASK 的定义为:
# define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))
上面表示 1 向左移动 cpu % __NCPUBITS 位。然后,将这个位或起来(置位)。

所以,CPU_SET 中的第 1 个 参数表示的是第 X 个 CPU,而不是一共多少个 CPU 。

从下面的 C 代码也可以看到这一点:
#define __USE_GNU
#include <sched.h>
#include <stdio.h>

int main()
{
     cpu_set_t cpuset;
     cpu_set_t *cpusetp = &cpuset;
     int *p = (int *)cpusetp;
     
     CPU_ZERO (&cpuset);

     CPU_SET (0, &cpuset);
     printf ("%d\\n", p[0]);

     
     CPU_SET (1, &cpuset);
     printf ("%d\\n", p[0]);
     

     CPU_SET (2, &cpuset);
     printf ("%d\\n", p[0]);


     CPU_SET (3, &cpuset);
     printf ("%d\\n", p[0]);

     CPU_SET (256, &cpuset);
     printf ("%d\\n", p[8]);
     

     return (0);
}
编译方法:
gcc -g -D_GNU_SOURCE cpu_set_t.c -o cpu_set_t
运行与输出:
./cpu_set_t 1
3
7
15
1
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-2 23:24 , Processed in 0.068662 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表