我一直都对位运算感兴趣,但凡最小的bit都能干一件大事,比如最多见的“存放一种状态”。
果然瘦子都比胖子灵活,就连计算机也没有例外,位运算巧妙的用着计算机的与门、或门、非门和异或等简单操作,就将空间开销拉到了最低。
碰巧正研究着kernel的网络协议栈,看到了中间也参杂着不少的位运算函数,所以仗着捣鼓TCP的机会,学习下内核的一些位运算机制。一是学到点自己感兴趣的东西,二是以后再遇到,自然可以相视一笑。
此外,内核的位运算实现大多都是原子操作,这让这些位运算函数被广泛用于内核的各个地方。
写到这,回想下,自己熟悉的原子操作只是操作系统“锁”和“信号量”的PV原子操作,惊!!!
test_and_set_bit
函数在内核中很常见,最近一次看见是在 “TCP-TSQ机制” 中,socket被用户持有,还没有被释放,tcp_tsq_handler
函数将暂停当前的数据流程,等待控制流程的结束,当前socket会被打上TCP_TSQ_DERFERRED延迟标识,用的正是test_and_set_bit。
test_and_set_bit最多的用途就是“存放一种状态”。
位运算test_and_set_bit的本质
test_and_set_bit函数的机制是设置addr指针所指向对象的第nr位,并返回原先nr位上的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/** * test_and_set_bit - Set a bit and return its old value * @nr: Bit to set * @addr: Address to count from */ static inline int test_and_set_bit(int nr, unsigned long *addr) { // BIT掩码 unsigned long mask = BIT_MASK(nr); // 原addr地址 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old; old = *p; // 原地址和掩码的或——1/0置1,1/1置0 *p = old | mask; // 原地址和掩码的与——1/1置1,1/0置0,即返回原值 //语句亦可用return old & mask; return (old & mask) != 0; }
test_and_set_bit 用地址和bit位掩码就搞定,光看代码还犯嘀咕,我下面再来张图。
应该很清楚了,没有太多复杂的调度,一切都那么直观,而内核也一直是这么做的。
至于BIT_MASK(nr)
,BIT_WORD(nr)
我特意去内核源码找了下,翻出来给大家看看。
1
2
3
4
5
6
7
8/* 宏运算 * BIT_PER_LONG是long类型的位长度 * 那么BIT_MASK很清楚是nr位的置1 * BIT_WORD是位和BIT_PER_LONG的余数,往往BIT_WORD(nr)是0 */ #define BIT_MASK(nr) (UL(1) << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
好了,就先到这里,“肥西”有点小情绪了,我得花点时间去照顾我的猫了。
最后
以上就是魁梧狗最近收集整理的关于学了手内核的位运算(1):test_and_set_bit的全部内容,更多相关学了手内核内容请搜索靠谱客的其他文章。
发表评论 取消回复