中mask设置_get_cpu_mask中的小技巧

4f29d36874ff3684db135d306e6afc25.png

内核版本

  • linux-v5.7

今天在阅读内核源码时,对get_cpu_mask()函数中使用的小技巧颇有感触,在此记录一下。

一、背景知识

get_cpu_mask()函数的作用是依据输入的cpu编号,返回对应的struct cpumask结构体型指针;cpumask是代表了系统中cpu集合的位图,每一bit个代表一个cpu。

可用以下两种方法创建cpumask:

  • 第一种是使用struct cpumask或者其别名cpumask_t定义一个变量,结构体定义如下:
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

struct cpumask结构体中只有一个成员bits,为一个bitmap;DECLARE_BITMAP宏定义入如下:

#define DECLARE_BITMAP(name,bits) 
	/* BITS_TO_LONGS宏的作用为计算bits需要几个long空间存储:
         * 		1、64位系统中long为8bytes,即64bits
         *		2、传入的bits范围在[1,64] ==> 返回1
         *		3、传入的bits范围在[65,128] ==> 返回2,以此类推
         */
        unsigned long name[BITS_TO_LONGS(bits)]

可见,bits本质为一个数组,数组大小由编译时设置的cpu个数NR_CPUS决定。

  • 第二种方法是先定义一个unsigned long型数组,然后使用to_cpumask宏转换为struct cpumask,其中to_cpumask宏定义如下:
#define to_cpumask(bitmap)                                              
        ((struct cpumask *)(1 ? (bitmap)                                
                            : (void *)sizeof(__check_is_bitmap(bitmap))))

可见,在运行时,该三目运算符永远返回bitmap,然后直接强制转换为struct cpumask *型;那么三目运算符后面的式子是干嘛的呢?先看__check_is_bitmap()函数定义:

static inline int __check_is_bitmap(const unsigned long *bitmap)
{
        return 1;
}

更加迷惑了,直接返回1;其实其不作用在运行时,而是在编译阶段检测传入的bitmap是否为unsigned long *类型,如不是则会产生编译警告。

二、get_cpu_mask实现

接下来是本文的主角:get_cpu_mask()函数,先贴出代码实现:

/* BITS_PER_LONG:long型变量所占的bit数,64位系统下为64bits */
extern const unsigned long
        cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)];

static inline const struct cpumask *get_cpu_mask(unsigned int cpu)
{       
        const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG];
        p -= cpu / BITS_PER_LONG;
        return to_cpumask(p);   
}

代码很简洁,只有三行,先来看cpu_bit_bitmap数组,其为一个二维数组,第一维大小固定为65,第二位与struct cpumask一样,依据NR_CPUS值而定,具体定义如下:

#define MASK_DECLARE_1(x)       [x+1][0] = (1UL << (x))
#define MASK_DECLARE_2(x)       MASK_DECLARE_1(x), MASK_DECLARE_1(x+1)
#define MASK_DECLARE_4(x)       MASK_DECLARE_2(x), MASK_DECLARE_2(x+2)
#define MASK_DECLARE_8(x)       MASK_DECLARE_4(x), MASK_DECLARE_4(x+4)

const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {

        MASK_DECLARE_8(0),      MASK_DECLARE_8(8),
        MASK_DECLARE_8(16),     MASK_DECLARE_8(24),
#if BITS_PER_LONG > 32
        MASK_DECLARE_8(32),     MASK_DECLARE_8(40),
        MASK_DECLARE_8(48),     MASK_DECLARE_8(56),
#endif 
};      
EXPORT_SYMBOL_GPL(cpu_bit_bitmap);

将宏全部展开(使用gcc进行预处理),即为:

const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {

        [0 +1][0] = (1UL << (0)), [0 +1 +1][0] = (1UL << (0 +1)),
        [0 +2 +1][0] = (1UL << (0 +2)), [0 +2 +1 +1][0] = (1UL << (0 +2 +1)),
        ...
        [56 +4 +2 +1][0] = (1UL << (56 +4 +2)), [56 +4 +2 +1 +1][0] = (1UL << (56 +4 +2 +1)),
}; 

只对cpu_bit_bitmap数组中的cpu_bit_bitmap[x][0]进行了初始化(x范围为[1,64]),假设传入的NR_CPUS范围在[65,128],数组定义即为cpu_bit_bitmap[65][2],其在内存中的布局如下:

71d6e79ce1ccb5ee5ccd3c9c85ad8f6c.png

蓝色部分为赋了初值的部分,初始值大小为1UL<<(x-1);灰色部分为未赋初值的部分;虚线部分的cpu_bit_bitmap[0]未赋初值。

再回头看get_cpu_mask()函数实现,举例说明如下:

  • 假设传入的cpu编号为1,get_cpu_mask()函数处理如下:
const unsigned long *p = cpu_bit_bitmap[1 + 1];
p -= 0;
return to_cpumask(p);

返回的cpumask如图所示:

20ba0c5bc85727f7d24cc3cdb8e4da64.png

cpumask中第二个bit置位,即代表cpu1。

  • 假设传入的cpu编号为65,get_cpu_mask()函数处理如下:
/* 初始值还是cpu_bit_bitmap[2] */
const unsigned long *p = cpu_bit_bitmap[1 + 1];
/* 因指针p指向的数据类型为unsigned long型,所以此处p指向的地址
 * 需减去1*sizeof(unsigned long),64为系统中即为8
 */
p -= 1;
return to_cpumask(p);

返回的cpumask如图所示:

f186d6b4b925b556aae068c1b8dd87f6.png

cpumask中第65个bit置位,即代表cpu65。

当NR_CPUS为[129,192],传入的cpu编号在[128,191]时,p指向的地址需减去2*sizeof(unsigned long);当NR_CPUS为[193,256],传入的cpu编号在[192,255]时,p指向的地址需减去3*sizeof(unsigned long),依此类推;另外cpu_bit_bitmap[0]为何不赋初值也就显而易见了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值