linux kernel 比较数据大小的宏定义

在分支预测,hash value计算,tree insert的时候,经常需要比较大小。这是一个高频率事件。 计算准确性,关系到系统稳定性。

普通程序员,写的关系运算符宏

#define min(a,b) ((a) < (b) ? (a) : (b))

linux kernel写的关系运算符宏:

c 复制代码
/* Format: __UNIQUE_ID_<name>_<__COUNTER__> */
#define __UNIQUE_ID(name)					\
	__PASTE(__UNIQUE_ID_,					\
	__PASTE(name,						\
	__PASTE(_, __COUNTER__)))

#define __cmp_op_min <
#define __cmp_op_max >

#define __cmp(op, x, y)	((x) __cmp_op_##op (y) ? (x) : (y))

#define __cmp_once_unique(op, type, x, y, ux, uy) \
	({ type ux = (x); type uy = (y); __cmp(op, ux, uy); })

#define __cmp_once(op, type, x, y) \
	__cmp_once_unique(op, type, x, y, __UNIQUE_ID(x_), __UNIQUE_ID(y_))

/**
 * min_t - return minimum of two values, using the specified type
 * @type: data type to use
 * @x: first value
 * @y: second value
 */
#define min_t(type, x, y) __cmp_once(min, type, x, y)


static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct kernfs_open_file *of = kernfs_of(iocb->ki_filp);
	ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE);
	const struct kernfs_ops *ops;
	char *buf;

	...
}

假设iov_iter_count(iter)的当前值为3,PAGE_SIZE的值为4096

其中的一行代码 ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE); 被展开之后是:

c 复制代码
ssize_t len = ({
	size_t __UNIQUE_ID_x____COUNTER__ = (iov_iter_count(iter));
	size_t __UNIQUE_ID_y____COUNTER__ = (PAGE_SIZE);
	((__UNIQUE_ID_3____COUNTER__) < (__UNIQUE_ID_4096____COUNTER__) ? :  __UNIQUE_ID_3____COUNTER__, __UNIQUE_ID_4096____COUNTER__)
});

为啥费劲巴拉,搞这么复杂,内核开发者是有病吗? 显然不是。 有2个bug:

bug 1,重复求值:

众所周知,宏是string_a替换为string_b,不带任何逻辑推导功能

min(++a, b); 作者原本的意思是: a=a+1; (a<b)? a : b // a自增一次之后,与b比较,求a和b的最小值

但宏被展开之后: (++a < b) ? ++a : b // a 会自增两次! 而不是自增1次

bug 2,数据类型隐式转换:

c 复制代码
int a = -5;
unsigned int b = 100;
unsigned int r = min(a, b);		// 人脑预期,r = -5
								// gcc 编译器的操作,把-5从int类型提升为unsigned int类型,a = UINT_MAX - 4,   显然a > 100,计算结果r = 100

bug 3,副作用问题:

如果用这样的宏定义,可以避免bug1、bug2

c 复制代码
#define min_t(type, x, y) ({ \
    type _x = x; \
    type _y = y; \
    _x < _y ? _x : _y; \
})

// 但同一函数里写两次 min_t,编译器就会报_x, _y多次定义的错误

c 复制代码
size_t a = min_t(size_t, v1, v2);
size_t b = min_t(size_t, v3, v4);

因此,内核开发者苦心孤诣,搞了这么一套复杂、灵活的宏,彻底解决了上面3个bug

花费2个小时才看懂的设计。

普通程序员,一行代码搞定。

(又蠢又自信那种,清澈的蠢)

​linux kernel程序员,来一套复杂的组合拳,精准、全面、无bug。

(算尽天机不自持,方兴未艾或可知)

​

​这个就是差距所在。2类程序员有云泥之别。

相关推荐
程序开发阿元16 分钟前
密码不进工具:一个独立开发者的服务器凭据管理办法
linux·ssh
拂拉氏43 分钟前
【知识讲解-题目讲解】 统计满足K约束的子字符串数量2 (滑动窗口+前缀和+二分查找+分析)
算法·前缀和·二分查找·滑动窗口
AR-26710-4 小时前
Linux Day33——定时任务+lvm
linux·运维
墨痕诉清风4 小时前
SSRF漏洞实战记录+OSS桶命令执行+MCP漏洞
linux·服务器·数据库
byte轻骑兵4 小时前
实战 4:BlueZ 源码中添加自定义设备过滤规则
linux·物联网·bluez·电脑蓝牙·嵌入式蓝牙
通信小呆呆5 小时前
电子战抗干扰技术:当“不被干扰”成为一种系统
算法·通信·mimo·isac
笑容掩饰我的失落5 小时前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
瓦瓦gogo5 小时前
从一个 TypeScript 函数开始理解 MCP 协议
linux·ubuntu·typescript
Severus_black6 小时前
【C++初阶】Stack、Queue算法题大练兵!
c语言·c++·算法·stl