Linux内核的percpu变量如何精确取值?

我选择signed int类型的percpu变量作为Linux内核中的半连接数量的统计值,我没有选择atomic类型的全局变量,我更是没有采用spinlock去保护一个一般的unsigned int类型的全局变量。

之所以选择signed int类型的percpu变量是因为要考虑到连接的处理在不同CPU之间的迁移。

到此为止,percpu变量的一切都很完美,轻量且无锁的操作是它作为统计计数器的最大优势。作为统计计数器,我们在读数的时候是允许一定的误差的,所以我们一般采用无锁的方式去读数:

for_each_possible_cpu(i) {
	res += per_cpu_ptr(ptr, i);
}

我们知道,以上语句的执行是需要时间的,无论遍历所有CPU有多快,但也是需要时间的,在这段时间内,percpu变量就可能发生遍历累加逻辑在执行期间无法捕捉的变化。

幸亏这些统计值是给人来读的,而人读这些统计值的目的是通过数值去导出一些规律,通常对数据分辨率的要求不会达到毫秒以下。

然而,如果内核需要这些值怎么办?

比如内核依照这些变量的值来执行是否释放一个数据对象的逻辑:

for_each_possible_cpu(i) {
	res += per_cpu_ptr(ptr, i);
}
if (res == 0) 
	free_something(...);

以上的代码显然是危险⚠️的,那么怎么办?

很简单,一把读写锁即可。但是和通常的读写锁的用法正好相反:

  • 更新percpu变量时拿read锁。
  • 读取percpu变量时拿write锁。

更新逻辑如下:

signed int *per_cpu_counter = per_cpu_ptr(..., this_cpu);
...
read_lock(&percpu_wrlock);
*per_cpu_counter ++;
read_unlock(&percpu_wrlock);

读取逻辑如下:

write_lock(&percpu_wrlock);

for_each_possible_cpu(i) {
	res += per_cpu_ptr(ptr, i);
}
if (res == 0) 
	free_something(...);

write_unlock(&percpu_wrlock);

虽然说,read/write lock期间会禁用抢占,并且会插入屏障,但具体到特定的体系结构以及具体的Linux内核平台,这些看似会 影响性能 的操作大多数都是可以忽略的,比方说,大部分的服务器内核都是关闭内核抢占选项来编译的。


如果从总体上看而不是抠细节的话,遍历所有CPU并取出percpu变量累加的过程是非常快的,至少这是一个 O ( 1 ) O(1) O(1)操作,更何况,绝大多数机器的CPU基本也就是3位数量级以下,所以这个 O ( 1 ) O(1) O(1)的常数值本身也是非常小的。

经理不待见trick,经理也不待见遍历,哪怕for一个20也不行。

2007年夏天,经理说第一次慢,经理不管DNS,所以一个工人就把北京一个机房的服务器IP写死在代码里了。后来这个服务器搬迁了,这个工人也离职了,事情到了我这里,我查到了这个硬编码,但是我很认同这种做法,虽然我更倾向于用配置文件而不是代码的宏定义…

我在配置文件里写死了20个常用的IP地址,前10个是服务器的IP,后10个是我首选的10个DNS,然后就上线了,效果真不错!

但是经理要看代码,经理看见了一个for,for了20个IP,经理说这个循环是耗时的,经理非要让我改,我百口难辩,于是,我改成了下面的样子:

if (是IP1)
	...
else if (是IP2)
	...
else if (是IP3)
	...
else if (是IP4)
	...
else if (是IP5)
	...
else if (是IP6)
	...
	...
else if (是IP20)
	...
else 
	...

OK,消除了循环,增加了代码量,大家唱着歌,下班了。


浙江温州皮鞋湿,下雨进水不会胖。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值