打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Linux原子操作的源代码分析[下]

---------------------------------------------------------------- 
find_next_zero_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能: 
find_next_zero_bit - find the first zero bit in a memory region 
但是参数条件为:@offset指定了开始搜索的位号(bitnumber)。在一段连续的内存中找到第一个为'0'的位。 

2.参数: 
@addr: The address to base the search on 
@offset: The bitnumber to start searching at 
@size: The maximum size to search 

int find_next_zero_bit(const unsigned long *addr, int size, int offset); 
内核中这个文件只给出了函数的声明,并没给出实现代码。 
-------------------------------------------------------------- 
我从几个不同CPU种类的头文件中抽取的代码来为例子来仔细的研究: 
-------------------------------------------------------------- 
include\asm-v850\bitops.h ffz() 
-------------------------------------------------------------- 
1.功能:ffz = Find First Zero in word.在双字中找到第一个为'0'的位。 

2.注意:Undefined if no zero exists.The functions are not atomic. 
对全'1'这种情况无定义,但是由于右移时,最高位填充'0',while循环可以结束,这时返回result == 32,也就是全'1'的这种情况。 

extern __inline__ unsigned long ffz (unsigned long word) 

unsigned long result = 0; 

while (word & 1) 

result++; 
word >>= 1; 

return result; 

-------------------------------------------------------------- 
include\asm-m32r\bitops.h ffz() 
-------------------------------------------------------------- 
功能,注意同前. 
static __inline__ unsigned long ffz(unsigned long word) 

int k; 

word = ~word; 
k = 0; 
if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
if (!(word & 0x00000001)) { k += 1; } 

return k; 

分析: 
1.问题转化: 
在word中寻找为第一个为'0'的位 
word = ~word;这就使问题转化为在word中寻找为第一个为'1'的位 

2.if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
==>如果第一个为'1'的位在低16位上,则(word & 0x0000ffff)为非0;则!(word & 0x0000ffff) == 0; 
==>如果第一个为'1'的位在高16位上,则(word & 0x0000ffff)为0;则!(word & 0x0000ffff) == 1;这时将k += 16; word >>= 16;即:k计位器加上16(因为低16位无'1'位);然后再将高16位右移16位,于是高16位变成了低16位; 

3.if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
同理,再对这个低16位(本来的或是经过右移高16位过来的)进行类似上面的运算,不过这时以8位为界。 
==>如果第一个为'1'的位在低8位上,则(word & 0x000000ff)为非0;则!(word & 0x000000ff) == 0; 
==>如果第一个为'1'的位在高8位上,则(word & 0x000000ff)为0;则!(word & 0x000000ff) == 1;这时将k += 8; word >>= 8;即:k计位器再加上8(因为低8位无'1'位);然后再将高8位右移8位,于是高8位变成了低8位; 

4.类似上面的,下面分别以4位,2位,1位来分界之:若条件满足则将高4位,2位,1位右移至低4位,2位,1位,同时位计数器相应相加。 
if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
if (!(word & 0x00000001)) { k += 1; } 

5.带几个数进行运算,验证程序逻辑: 
本是要找word中的第一个为'0'的位置<===>找~word中的第一个为'1'的位置: 
--------------------------------------------------------------- 
word == ~word == 0x00000001 (即word == 0xfffffffe) 
执行如下: 
if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
条件不满足,k == 0, word == 0x00000001 

if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
条件不满足, k == 0, word == 0x00000001 

if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
条件不满足, k == 0, word == 0x00000001 

if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
条件不满足, k == 0, word == 0x00000001 

if (!(word & 0x00000001)) { k += 1; } 
条件满足, k == 1, word == 0x00000001 
--------------------------------------------------------------- 

word == ~word == 0x10000000 
执行如下: 
if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
条件满足,k == 16, word == 0x00001000 

if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
条件满足, k == 24, word == 0x00000010 

if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
条件满足, k == 28, word == 0x00000008 

if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
条件满足, k == 30, word == 0x00000002 

if (!(word & 0x00000001)) { k += 1; } 
条件满足, k == 31, word == 0x00000001 
--------------------------------------------------------------- 

word == ~word == 0x00ff0000 
执行如下: 
if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
条件满足,k == 16, word == 0x000000ff 

if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
条件不满足, k == 16, word == 0x000000ff 

if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
条件不满足, k == 16, word == 0x000000ff 

if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
条件不满足, k == 16, word == 0x000000ff 

if (!(word & 0x00000001)) { k += 1; } 
条件不满足, k == 16, word == 0x000000ff 
--------------------------------------------------------------- 
word == ~word == 0x0000 f000 
执行如下: 
if (!(word & 0x0000 ffff)) { k += 16; word >>= 16; } 
条件不满足,k == 0, word == 0x0000 f000 

if (!(word & 0x0000 00ff)) { k += 8; word >>= 8; } 
条件满足, k == 8, word == 0x0000 00f0 

if (!(word & 0x0000 000f)) { k += 4; word >>= 4; } 
条件满足, k == 12, word == 0x0000 000f 

if (!(word & 0x0000 0003)) { k += 2; word >>= 2; } 
条件不满足, k == 12, word == 0x0000 000f 

if (!(word & 0x0000 0001)) { k += 1; } 
条件不满足, k == 12, word == 0x0000 000f 
-------------------------------------------------------------- 
word == ~word == 0x05 00 00 00 0000 0101 ... 
执行如下: 
if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } 
条件满足,k == 16, word == 0x0000 0500 

if (!(word & 0x000000ff)) { k += 8; word >>= 8; } 
条件满足, k == 24, word == 0x0000 0005 

if (!(word & 0x0000000f)) { k += 4; word >>= 4; } 
条件不满足, k == 24, word == 0x0000 0005 

if (!(word & 0x00000003)) { k += 2; word >>= 2; } 
条件不满足, k == 24, word == 0x0000 0005 

if (!(word & 0x00000001)) { k += 1; } 
条件不满足, k == 24, word == 0x0000 0005 
-------------------------------------------------------------- 
find_next_zero_bit() 
-------------------------------------------------------------- 
@addr: The address to base the search on 
@offset: The bitnumber to start searching at 
@size: The maximum size to search 
set,res,offset,bit以位为单位; 
p,addr,以字节为单位; 
功能:从offset所指向的位开始,在一段连续的内存中,找从这里起(含offset这一位)的后面第一个为'0'的位。 
static __inline__ int find_next_zero_bit(void *addr, int size, int offset) 

unsigned long *p = ((unsigned long *)addr) + (offset >> 5); 
int set = 0, bit = offset & 31, res; 
if (bit) 

/* 
* Look for zero in first byte 
*/ 
__asm__("bsfl %1,%0\n\t" 
"jne 1f\n\t" 
"movl $32, %0\n" 
"1:" 
: "=r"(set) 
:"r"(~ (*p >> bit))); 
if (set < (32 - bit)) 
return set + offset; 
set = 32 - bit; 
p++; 

/* 
* No zero yet, search remaining full bytes for a zero 
*/ 
res = find_first_zero_bit(p, size - 32 * (p - (unsigned long *)addr)); 
return (offset + set + res); 


代码分析: 
1.bsfl汇编指令: 
intel汇编指令:bsf oprd1,oprd2; 
顺向位扫描(bit scan forward) 
从右向左(从位0-->位15或位31)扫描字或双字操作数oprd2中第一个含"1"的位,并把扫描到的第一个含'1'的位的位号送操作数oprd1 
AT&T格式汇编指令bsfl类似bsf,只是源操作数和目的操作数顺序相反。 

2.unsigned long *p = ((unsigned long *)addr) + (offset >> 5); 
(offset >> 5)将offset位数转换为以4B计的unsigned long型。从而将p移动到offset所指向的起始位所处的那个unsigned long型4B双字的开始处。 

3.int set = 0, bit = offset & 31, res; 
set:第一个'0'所在的位(第一个'1'所在的位)距离offset所指向的位距离(以位计) 

res:表示以p所指向的位为标准调用find_first_zero_bit()得到的函数的返回值,即:p所指向的位离p所指向的位的后面的第一个'0'位的距离(以位计) 

bit = offset & 31 :表示保留offset的最低5位,也即:0-31,也就是:offset所指向的起始位位于所处在的那个unsigned long型4B双字中的第几位。 

4.if (bit) : 
offset所指向的位所处在的那个unsigned long型4B双字中的第x位,x != 0,即:offset所指向的起始位所处在的那个unsigned long型4B双字中的非首位. 

5.__asm__("bsfl %1,%0\n\t" 
"jne 1f\n\t" 
"movl $32, %0\n" 
"1:" 
: "=r"(set) 
:"r"(~ (*p >> bit))); 

进入汇编指令前的初始条件: 
p:指向offset所指向的起始位所处的那个unsigned long型4B双字的开始处。 
bit: offset所指向的起始位位于所处在的那个unsigned long型4B双字中的第几位。 

*p >> bit : 将指向offset所指向的起始位所处的那个unsigned long型4B双字的开始处的指针右移bit位,即:将offset所指向的起始位前面的位全部移出去,而将offset所指向的起始位右移到了最右端(最低位,0位),该起始位成了该unsigned long型4B的首位了。 
~ (*p >> bit) :将各位取反,本来是要找从offset位开始的后面的第一个'0'位,这一取反就将问题转化为找从offset位开始的后面的第一个'1'所在的位。 

bsfl %1,%0 <====> bsfl (~ (*p >> bit)),set 
找(~ (*p >> bit))中的第一个'1'所在的位,将结果存放在set中返回。 

movl $32, %0 : 
当从offset位开始的后面的32个位为全'0',即:取反前为全'1',这时置set = 32 
-------------------------------------------------------------- 
6.有三种情况: 
1)set,res,offset,bit以位为单位; 
p,addr,以字节为单位; 
unsigned long *p = ((unsigned long *)addr) + (offset >> 5); 
int set = 0, bit = offset & 31, res; 
addr:指向首地址,p指向指向offset所指向的起始位所处的那个unsigned long型4B双字的开始处。offset指向的待search的起始位'*'。bit: offset所指向的起始位位于所处在的那个unsigned long型4B双字中的第几位。
此时bit不为0,执行if体。 
if (bit) 

/* 
* Look for zero in first byte 
*/ 
__asm__("bsfl %1,%0\n\t" 
"jne 1f\n\t" 
"movl $32, %0\n" 
"1:" 
: "=r"(set) 
:"r"(~ (*p >> bit))); 
经过if中的汇编指令的执行,得到了set,'*'距离'0'的距离长度。由于此时,以offset所指向的search起始位为unsigned long 型4B的首位。于是,'0'的位置可能在以p所指的32位中,也可在其外,这里考虑在其内的情况。 
p offset 
| | 
| | 
|----32-----| 
| | 32-bit| 
| | | 
addr | | | 
| |bit|set| | 
| | | | | 
------------------------------ 
| | * '0' | | 
| | | | 
------------------------------ 
| | 
| | 
|-----------size-------------| 
if (set < (32 - bit)) 
return set + offset; 
由于set < (32 - bit),所以返回set + offset.从图知这就是自'*',offset起始处后第一个为'0'处。 

-------------------------------------------------------------- 
2)unsigned long *p = ((unsigned long *)addr) + (offset >> 5); 
int set = 0, bit = offset & 31, res; 
addr:指向首地址,p指向指向offset所指向的起始位所处的那个unsigned long型4B双字的开始处。offset指向的待search的起始位'*'。bit: offset所指向的起始位位于所处在的那个unsigned long型4B双字中的第几位。 
p offset p(p++) 
| | | 
| | | 
|----32-----| 
| | 32-bit| 
| | | 
addr | | |res| 
| |bit|--set--| | 
| | | | | 
------------------------------ 
| | * | '0' | 
| | | | 
------------------------------ 
| |-set调整前-| | 
| | 
|-----------size-------------| 
此时bit不为0,执行if体。经过if中的汇编指令的执行,得到了‘set调整前’距离'0'的距离长度。由于此时,以offset所指向的search起始位为unsigned long 型4B的首位。于是,'0'的位置可能在以p所指的32位中,也可在其外,这里考虑在其外的情况。 
if (bit) 

/* 
* Look for zero in first byte 
*/ 
__asm__("bsfl %1,%0\n\t" 
"jne 1f\n\t" 
"movl $32, %0\n" 
"1:" 
: "=r"(set) 
:"r"(~ (*p >> bit))); 
当'0'在p所指向的32位其外,即上图情况时候。经汇编指令得到的set值,设为‘set调整前’ > (32 - bit)见上图。则进行set调整set = 32 - bit;见上图set.然后p++,p移动32位,指向下一个unsigned long型4B。 
if (set < (32 - bit)) 
return set + offset; 
set = 32 - bit; 
p++; 

res:见上图示意,易懂。 
res = find_first_zero_bit(p, size - 32 * (p - (unsigned long *)addr)); 

return (offset + set + res); 
/* 
* No zero yet, search remaining full bytes for a zero 
*/ 
res = find_first_zero_bit(p, size - 32 * (p - (unsigned long *)addr)); 
return (offset + set + res); 
-------------------------------------------------------------- 
3)若offset位于unsigned long边界上,见图所示: 
unsigned long *p = ((unsigned long *)addr) + (offset >> 5); 
int set = 0, bit = offset & 31, res; 
分析:bit == 0,set == 0 

if (bit) 条件不成立。 

res = find_first_zero_bit(p, size - 32 * (p - (unsigned long *)addr)); 
return (offset + set + res)<==>return(offset + res) 

offset 



|----32-----| 
| | 
| | 
addr | | 
| | | 
| |-res--| | 
------------------------------ 
| * '0' | | 
| | | | 
------------------------------ 
| | 
| | 
|-----------size-------------| 
--------------------------------------------------------------- 
__ffs() 
---------------------------------------------------------------- 
功能:__ffs - find first bit '1''s bit number in word.在一个双字中找到第一个为'1'的位。 

参数:@word: The word to search 

说明: Undefined if no bit exists, so code should check against 0 first. 

static inline unsigned long __ffs(unsigned long word) 

__asm__("bsfl %1,%0" 
:"=r" (word) 
:"rm" (word)); 
return word; 

---------------------------------------------------------------- 
\include\asm-m68knommu\bitops.h ffs() 
---------------------------------------------------------------- 
此处的ffs仍是用C语言写的,特地收入进来进行研究: 
内核注释:Generic ffs(). 

static inline int ffs(int x) 

int r = 1; 

if (!x) 
return 0; 
if (!(x & 0xffff)) {x >>= 16; r += 16; } 
if (!(x & 0xff)) {x >>= 8; r += 8; } 
if (!(x & 0xf)) {x >>= 4; r += 4; } 
if (!(x & 3)) {x >>= 2; r += 2; } 
if (!(x & 1)) {x >>= 1; r += 1; } 
return r; 

比较:ffs():找双字中第一个为'1'的位 
ffz():找双字中第一个为'0'的位 
---------------------------------------------------------------- 
find_first_bit() 
---------------------------------------------------------------- 
功能:find the first set bit in a memory region,其实是在一段连续的内存中找到第一个为'1'的位。 

参数:@addr: The address to start the search at 
@size: The maximum size to search(以位计) 

返回值:Returns the bit-number of the first set bit, not the number of the byte containing this bit. 

static inline int find_first_bit(const unsigned long *addr, unsigned size) 

int x = 0; 

while (x < size) 

unsigned long val = *addr++; 
if (val) 
return __ffs(val) + x; 
x += (sizeof(*addr)<<3); 

return x; 

在const的作用下,addr可以变,但addr所指向的内容不能变。 
---------------------------------------------------------------- 
比较异同 
---------------------------------------------------------------- 
find_first_zero_bit(const unsigned long *addr, unsigned size): find the first zero bit in a memory region 
在连续的内存中找到第一个为0的位,并将该位的序号返回。 

ffz(unsigned long word): Find First Zero in word.在双字中找到第一个为'0'的位。 

find_next_zero_bit(void *addr, int size, int offset):从offset所指向的位开始,在一段连续的内存中,找从这里起(含offset这一位)的后面第一个为'0'的位。 
这三个函数是找第一个为'0'的位,但找的对象一个为连续的内存,一个为双字,还有一个虽是双字,但是却指定了起始位置:offset。 
---------------------------------------------------------------- 
__ffs(unsigned long word): find first bit '1''s bit number in word.在一个双字中找到第一个为'1'的位。 

find_first_bit(const unsigned long *addr, unsigned size):是在一段连续的内存中找到第一个为'1'的位。 
这二个函数是找第一个为'1'的位,但找的对象一个为连续的内存,一个为双字, 
---------------------------------------------------------------- 
ffz() 
---------------------------------------------------------------- 
本是在双字中找到第一个为'0'的位,但~word,则为'0'的位变为'1',则转化为找第一个为'1'的位。 
static inline unsigned long ffz(unsigned long word) 

__asm__("bsfl %1,%0" 
:"=r" (word) 
:"r" (~word)); 
return word; 

---------------------------------------------------------------- 
sched_find_first_bit() 
---------------------------------------------------------------- 
内核注释: 
Every architecture must define this function. It's the fastest way of searching a 140-bit bitmap where the first 100 bits are unlikely to be set. It's guaranteed that at least one of the 140 bits is cleared. 

static inline int sched_find_first_bit(const unsigned long *b) 

if (unlikely(b[0])) 
return __ffs(b[0]); 
if (unlikely(b[1])) 
return __ffs(b[1]) + 32; 
if (unlikely(b[2])) 
return __ffs(b[2]) + 64; 
if (b[3]) 
return __ffs(b[3]) + 96; 
return __ffs(b[4]) + 128; 

要快速设置长位(如:160位)中的高位中的位,如果从低位扫描上来就慢了。运用数组一次就以4B(32位)为单位来提取,这就只须对这4B,32位进行扫描,大大提高了效率。然后再运用unlikely()直接对可能性小的地方,让编译器优化之。 
---------------------------------------------------------------- 
ffs() 
---------------------------------------------------------------- 
ffs():find first bit '1''s bit-number in a word 
@x: the word to search 

static inline int ffs(int x) 

int r; 

__asm__("bsfl %1,%0\n\t" 
"jnz 1f\n\t" 
"movl $-1,%0\n" 
"1:" 
: "=r" (r) 
: "rm" (x)); 
return r+1; 

---------------------------------------------------------------- 
预备工作: \include\linux\bitops.h 
---------------------------------------------------------------- 
---------------------------------------------------------------- 
generic_hweight32() 
---------------------------------------------------------------- 
Hamming weight is the number of "1" bits in the binary sequence. 
/* 
* hweightN: returns the hamming weight (i.e. the number 
* of bits set) of a N-bit word. 
*/ 
求32位二进制数中的'1'的个数: 

static inline unsigned int generic_hweight32(unsigned int w) 

unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); 
res = (res & 0x33333333) + ((res >> 2) & 0x33333333); 
res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); 
res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); 
return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); 


分析: 
*************************************************************** 
1.具体分析: 
unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); 

分析: 
(w & 0x55555555) : 
因为0x55555555 == 0101 0101 0101 0101 0101 0101 0101 0101 
这是将w位上的第0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30位上的'1'保留下来,第1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31位清0 

((w >> 1) & 0x55555555) : 
将w右移一位后,奇数位变成偶数位,再将为'1'的位保留下来,即:将原始的w中的第1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31位中的'1'位保留下来 

分析:第一条代码: 
分析:(w & 0x55555555) 
设: 
w == 0000 1111 0101 1001 0110 1010 1000 0001 
0x55555555 == 0101 0101 0101 0101 0101 0101 0101 0101 & 
---------------------------------------------------------------- 
(w & 0x55555555): 0000 0101 0101 0001 0100 0000 0000 0001 

分析:((w >> 1) & 0x55555555) 
w >> 1 == 0000 0111 1010 1100 1011 0101 0100 0000 
0x55555555 == 0101 0101 0101 0101 0101 0101 0101 0101 & 
---------------------------------------------------------------- 
(w>>1)&0x55555555 == 0000 0101 0000 0100 0001 0101 0100 0000 

w中共计14个'1' 
而(w & 0x55555555)共计个7'1',(w & 0x55555555)共计个7'1'.所以很明显的(w & 0x55555555)将为'1'的偶数位保留下来而((w >> 1) & 0x55555555)将为'1'的奇数位保留下来。两者之和就是w中所有位中的为'1'的位。 

分析:res = (w & 0x55555555) + ((w >> 1) & 0x55555555): 
(w & 0x55555555): 0000 0101 0101 0001 0100 0000 0000 0001 
(w>>1)&0x55555555 == 0000 0101 0000 0100 0001 0101 0100 0000 + 
---------------------------------------------------------------- 
res == 0000 1010 0101 0101 0101 0101 0100 0001 

发现规律:只有当为'...11...'即相连的奇数位,偶数位都为'1'时,(w & 0x55555555) + ((w >> 1) & 0x55555555的相加的结果中才会出现1+1要进位的现象。若为'...10...'或'...01...'或'...00...'则均不会产生要进位的现象。 
在res中非相连的奇数位和偶数位的'1'均保留下来,而相连的奇数位和偶数位的'1'则会发生进位。 
*************************************************************** 
分析第二条代码: 
res = (res & 0x33333333) + ((res >> 2) & 0x33333333); : 

分析:(res & 0x33333333): 
(res & 0x33333333): 
res == 0000 1010 0101 0101 0101 0101 0100 0001 
0x33333333 == 0011 0011 0011 0011 0011 0011 0011 0011 & 
---------------------------------------------------------------- 
res & 0x33333333 == 0000 0010 0001 0001 0001 0001 0000 0001 

分析:((res >> 2) & 0x33333333) 
(res >> 2) == 0000 0010 1001 0101 0101 0101 0101 0000 
0x33333333 == 0011 0011 0011 0011 0011 0011 0011 0011 & 
---------------------------------------------------------------- 
res>>2&0x33333333 == 0000 0010 0001 0001 0001 0001 0001 0000 
这是(res>>2)&(0x33333333)的计算结果。上面由于空间不够所以没写括号。 

分析:res = (res & 0x33333333) + ((res >> 2) & 0x33333333);: 
(res & 0x33333333)保存了每4位(即一个16进制数)中低2位的'1'; 
((res >> 2) & 0x33333333)保存了每4位(即一个16进制数)中高2位的'1'; 
两者相加结果为下面: 
res & 0x33333333 == 0000 0010 0001 0001 0001 0001 0000 0001 
res>>2&0x33333333 == 0000 0010 0001 0001 0001 0001 0001 0000 + 
---------------------------------------------------------------- 
res == 0000 0100 0010 0010 0010 0010 0001 0001 

*************************************************************** 
分析第三条代码: 
res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); 

分析: (res & 0x0F0F0F0F) 
res & 0x0F0F0F0F: 
res == 0000 0100 0010 0010 0010 0010 0001 0001 
0x0F0F0F0F == 0000 1111 0000 1111 0000 1111 0000 1111 & 
---------------------------------------------------------------- 
res & 0x0F0F0F0F == 0000 0100 0000 0010 0000 0010 0000 0001 

分析:((res >> 4) & 0x0F0F0F0F): 
res >> 4 == 0000 0000 0100 0010 0010 0010 0010 0001 
0x0F0F0F0F == 0000 1111 0000 1111 0000 1111 0000 1111 & 
---------------------------------------------------------------- 
(res >> 4) & 
0x0F0F0F0F == 0000 0000 0000 0010 0000 0010 0000 0001 

分析:res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); 
(res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F)两者相加结果为下面: 
res & 0x0F0F0F0F == 0000 0100 0000 0010 0000 0010 0000 0001 
(res >> 4) & 
+ 0x0F0F0F0F == 0000 0000 0000 0010 0000 0010 0000 0001 + 
---------------------------------------------------------------- 
res == 0000 0100 0000 0100 0000 0100 0000 0010 

*************************************************************** 
分析第四条代码: 
res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); 

分析:(res & 0x00FF00FF): 
res == 0000 0100 0000 0100 0000 0100 0000 0010 
0x00FF00FF == 0000 0000 1111 1111 0000 0000 1111 1111 & 
---------------------------------------------------------------- 
res & 0x00FF00FF == 0000 0000 0000 0100 0000 0000 0000 0010 

分析:((res >> 8) & 0x00FF00FF): 
res >> 8 == 0000 0000 0000 0100 0000 0100 0000 0100 
0x00FF00FF == 0000 0000 1111 1111 0000 0000 1111 1111 & 
---------------------------------------------------------------- 
(res >> 8) & 
0x00FF00FF == 0000 0000 0000 0100 0000 0000 0000 0100 

分析:res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF): 
res & 0x00FF00FF == 0000 0000 0000 0100 0000 0000 0000 0010 
(res >> 8) & 
+ 0x00FF00FF == 0000 0000 0000 0100 0000 0000 0000 0100 + 
---------------------------------------------------------------- 
res == 0000 0000 0000 1000 0000 0000 0000 0110 

*************************************************************** 
分析第五条代码: 
(res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); 

分析:res & 0x0000FFFF 
res == 0000 0000 0000 1000 0000 0000 0000 0110 

0x0000FFFF == 0000 0000 0000 0000 1111 1111 1111 1111 
---------------------------------------------------------------- 
res & 0x0000FFFF == 0000 0000 0000 0000 0000 0000 0000 0110 


分析:(res >> 16) & 0x0000FFFF 
res >> 16 == 0000 0000 0000 0000 0000 0000 0000 1000 
0x0000FFFF == 0000 0000 0000 0000 1111 1111 1111 1111 
---------------------------------------------------------------- 
(res >> 16) & 
& 0x0000FFFF == 0000 0000 0000 0000 0000 0000 0000 1000 


分析:res = (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); 
res & 0x0000FFFF == 0000 0000 0000 0000 0000 0000 0000 0110 
(res >> 16) & 
& 0x0000FFFF == 0000 0000 0000 0000 0000 0000 0000 1000 + 
---------------------------------------------------------------- 
res == 0000 0000 0000 0000 0000 0000 0000 1110 

*************************************************************** 
2.算法研究: 
1)核心本质: 
本算法为递归算法,其思想如下: 
(1)递归的最小单元:两位二进制数中的'1'的个数的求法 
两位二进制数中的'1'的个数 == 奇数位'1'的个数 + 偶数位'1'的个数 

1-1:先用01 序列屏蔽掉奇数位'1',保留下偶数位'1',由于只有1位,没有'1'的位则是0,若有一个'1',则是1,也恰好就是'1'的个数。 

1-2:再将该二进制右移一位,这样奇数位到了偶数位,再用01010101... 序列屏蔽掉奇数位'1',保留下偶数位'1'(这时的偶数位就是移位前的奇数位),由于只有1位,为0则是0,为1则是1,也恰好就是'1'的个数。如果不右移一位,则奇数位的'1'的个数若为1的话,就会成为10,这就与'1'的个数不符了。 

1-3:将两者相加, 奇数位'1'的个数 + 偶数位'1'的个数 ==两位二进制数中的'1'的个数 

(2)递归下去: 
对4位数: 
将4位数以2位为一组,不就成了2位数,为奇数组,偶数组。然后处理方法同上。用0011,将奇数组清0,留下偶数组为1的位不动,为0的位自然也不动。这时的偶数组为2位二进制数,'1'的个数的求法同递归的最小单元:两位二进制数中的'1'的个数的求法。即可求出这个偶数组中'1'的个数。 
将该分了奇偶组的4位数,右移一组(2位,模拟2位二进制数的处理方法),同上处理方法就搞出了奇数组的'1'的个数。 
最后,偶数组的'1'的个数 + 奇数组的'1'的个数 == 该4位数中的'1'的个数 

同理:对8位数: 
以4位为分组,将8位数就分成了2组,每组4位,这样8位数就变成了2位数。模拟2位数的处理方法,求奇数组,偶数组里面的含'1'的个数,再将两者相加就是8位数中的'1'的个数。(注意:求奇数组中的含'1'的个数时候要将其右移4位,要不然求出的个数就会带有4个0000,结果就不对了)。每组为4位,又分为2组,每组为2位二进制数,又模拟2位数的处理方法,求新奇数组,新偶数组里面的含'1'的个数,再将两者相加就是这个4位数中的'1'的个数。对已经分到2位二进制数的组,已是最小的递归单元,直接可以求出,再层层递归上去就搞出了8位二进制数中的'1'的个数。 

对16位,32位,64位数,方法一样,只是再多分几组,将16位,32位,64位数都拆分为两组数,成一个两位的”二进制数“,再一一分组递归下去。 

---------------------------------------------------------------- 
generic_hweight16() 
---------------------------------------------------------------- 

static inline unsigned int generic_hweight16(unsigned int w) 

unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555); 
res = (res & 0x3333) + ((res >> 2) & 0x3333); 
res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); 
return (res & 0x00FF) + ((res >> 8) & 0x00FF); 

---------------------------------------------------------------- 
generic_hweight8() 
---------------------------------------------------------------- 

static inline unsigned int generic_hweight8(unsigned int w) 

unsigned int res = (w & 0x55) + ((w >> 1) & 0x55); 
res = (res & 0x33) + ((res >> 2) & 0x33); 
return (res & 0x0F) + ((res >> 4) & 0x0F); 

---------------------------------------------------------------- 
generic_hweight64() 
---------------------------------------------------------------- 

static inline unsigned long generic_hweight64(__u64 w) 

#if BITS_PER_LONG < 64 
return generic_hweight32((unsigned int)(w >> 32)) + 
generic_hweight32((unsigned int)w); 
#else 
u64 res; 
res = (w & 0x5555555555555555ul) + ((w >> 1) & 0x5555555555555555ul); 
res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); 
res = (res & 0x0F0F0F0F0F0F0F0Ful) + ((res >> 4) & 0x0F0F0F0F0F0F0F0Ful); 
res = (res & 0x00FF00FF00FF00FFul) + ((res >> 8) & 0x00FF00FF00FF00FFul); 
res = (res & 0x0000FFFF0000FFFFul) + ((res >> 16) & 0x0000FFFF0000FFFFul); 
return (res & 0x00000000FFFFFFFFul) + ((res >> 32) & 0x00000000FFFFFFFFul); 
#endif 

---------------------------------------------------------------- 
hweight_long() 
---------------------------------------------------------------- 

static inline unsigned long hweight_long(unsigned long w) 

return sizeof(w) == 4 ? generic_hweight32(w) : generic_hweight64(w); 

---------------------------------------------------------------- 
预备工作结束 
--------------------------------------------------------------- 
hweight32(x) hweight16(x) hweight8(x) 
-------------------------------------------------------------- 
#define hweight32(x) generic_hweight32(x) 
#define hweight16(x) generic_hweight16(x) 
#define hweight8(x) generic_hweight8(x) 
---------------------------------------------------------------- 
ext2_set_bit() 
---------------------------------------------------------------- 

#define ext2_set_bit(nr,addr) \ 
__test_and_set_bit((nr),(unsigned long*)addr) 
---------------------------------------------------------------- 
ext2_set_bit_atomic() 
---------------------------------------------------------------- 

#define ext2_set_bit_atomic(lock,nr,addr) \ 
test_and_set_bit((nr),(unsigned long*)addr) 
---------------------------------------------------------------- 
ext2_clear_bit() 
---------------------------------------------------------------- 

#define ext2_clear_bit(nr, addr) \ 
__test_and_clear_bit((nr),(unsigned long*)addr) 
---------------------------------------------------------------- 
ext2_clear_bit_atomic() 
---------------------------------------------------------------- 
#define ext2_clear_bit_atomic(lock,nr, addr) \ 
test_and_clear_bit((nr),(unsigned long*)addr) 
---------------------------------------------------------------- 
ext2_test_bit() 
---------------------------------------------------------------- 
#define ext2_test_bit(nr, addr) test_bit((nr),(unsigned long*)addr) 
---------------------------------------------------------------- 
ext2_find_first_zero_bit() 
---------------------------------------------------------------- 
#define ext2_find_first_zero_bit(addr, size) \ 
find_first_zero_bit((unsigned long*)addr, size) 
---------------------------------------------------------------- 
ext2_find_next_zero_bit() 
---------------------------------------------------------------- 
#define ext2_find_next_zero_bit(addr, size, off) \ 
find_next_zero_bit((unsigned long*)addr, size, off) 
---------------------------------------------------------------- 
minix_test_and_set_bit() 
---------------------------------------------------------------- 
/* Bitmap functions for the minix filesystem. */ 
#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,(void*)addr) 
---------------------------------------------------------------- 
minix_set_bit() 
---------------------------------------------------------------- 
#define minix_set_bit(nr,addr) __set_bit(nr,(void*)addr) 
---------------------------------------------------------------- 
minix_test_and_clear_bit() 
---------------------------------------------------------------- 
#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,(void*)addr) 
---------------------------------------------------------------- 
minix_test_bit() 
---------------------------------------------------------------- 
#define minix_test_bit(nr,addr) test_bit(nr,(void*)addr) 
---------------------------------------------------------------- 
find_first_zero_bit() 
---------------------------------------------------------------- 
#define minix_find_first_zero_bit(addr,size) \ 
find_first_zero_bit((void*)addr,size) 

*************************************************************** 
原子操作的第二个方面: 逻辑运算,32位平台-全部结束 
**************************************************************** 
-------------------------------------------------------------- 
原子操作的第二个方面: 逻辑运算,64位平台 
-------------------------------------------------------------- 
在64位平台上的原子操作的第二个方面: 逻辑运算的有关文件中,很多的函数,宏都是类似32位平台的,如果差不多的话就不再啰嗦了。 
-------------------------------------------------------------- 
-----------------------\asm-x86_64\bitops.h--------------------- 
#ifndef _X86_64_BITOPS_H 
#define _X86_64_BITOPS_H 

#include <linux/config.h> 
-------------------------------------------------------------- 
LOCK_PREFIX 
-------------------------------------------------------------- 
#ifdef CONFIG_SMP 
#define LOCK_PREFIX "lock ; " 
#else 
#define LOCK_PREFIX "" 
#endif 
-------------------------------------------------------------- 
ADDR 
-------------------------------------------------------------- 
#define ADDR (*(volatile long *) addr) 
-------------------------------------------------------------- 
set_bit() 
-------------------------------------------------------------- 
功能:set_bit - Atomically set a bit in memory 

参数: 
@nr: the bit to set 
@addr: the address to start counting from 

说明: 
1.This function is atomic and may not be reordered. 
2.@nr may be almost arbitrarily large; this function is not restricted to acting on a single-word quantity. 

static __inline__ void set_bit(int nr, volatile void * addr) 

__asm__ __volatile__( LOCK_PREFIX 
"btsl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 

1.I:0-31的常数 
2.d:使用寄存器edx 
3.bts oprd1,oprd2 (Intel) 位测试并置位指令,被测试位送CF并且被测试位置1 
oprd1:可以是16位或32位通用寄存器和16位或32位存储单元,用于指定要测试的内容。 
oprd2:必须是8位立即数或与操作数oprd1长度相等的通用寄存器,用于指定要测试的位。但AT&T的btsl oprd2,oprd1源/目地操作数顺序相反。 
-------------------------------------------------------------- 
__set_bit() 
-------------------------------------------------------------- 
说明:Unlike set_bit(), this function is non-atomic and may be reordered. 

static __inline__ void __set_bit(int nr, volatile void * addr) 

__asm__ volatile( 
"btsl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 

主要功能同set_bit()函数。 
-------------------------------------------------------------- 
clear_bit() 
-------------------------------------------------------------- 
功能:clear_bit - Clears a bit in memory 

参数: 
@nr: Bit to clear 
@addr: Address to start counting from 

说明: 
clear_bit() is atomic and may not be reordered. However, it does not contain a memory barrier, so if it is used for locking purposes,you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() in order to ensure changes are visible on other processors. 

static __inline__ void clear_bit(int nr, volatile void * addr) 

__asm__ __volatile__( LOCK_PREFIX 
"btrl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr)); 

btrl %1,%0 <==========> btrl nr,ADDR 
%1 == nr: 将要设置的位 
%0 == ADDR: 要被设置的操作数 
位测试并复位指令,被测试位送CF并且被测试位清0 
-------------------------------------------------------------- 
__clear_bit() 
-------------------------------------------------------------- 
static __inline__ void __clear_bit(int nr, volatile void * addr) 

__asm__ __volatile__( 
"btrl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr)); 

功能同上,只是无LOCK_PREFIX 
-------------------------------------------------------------- 
smp_mb__before_clear_bit() smp_mb__after_clear_bit() 
-------------------------------------------------------------- 
#define barrier() __asm__ __volatile__("": : :"memory") 

#define smp_mb__before_clear_bit() barrier() 
#define smp_mb__after_clear_bit() barrier() 
清位前/后的多处理器的内存屏障。 
-------------------------------------------------------------- 
__change_bit() 
-------------------------------------------------------------- 
功能: 
__change_bit - Toggle a bit in memory 

参数: 
@nr: the bit to change 
@addr: the address to start counting from 

说明: 
Unlike change_bit(), this function is non-atomic and may be reordered. 

static __inline__ void __change_bit(int nr, volatile void * addr) 

__asm__ __volatile__( 
"btcl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr)); 

-------------------------------------------------------------- 
change_bit() 
-------------------------------------------------------------- 
说明:change_bit() is atomic and may not be reordered.Note that @nr may be almost arbitrarily large; this function is not restricted to acting on a single-word quantity. 

static __inline__ void change_bit(int nr, volatile void * addr) 

__asm__ __volatile__( LOCK_PREFIX 
"btcl %1,%0" 
:"=m" (ADDR) 
:"dIr" (nr)); 

-------------------------------------------------------------- 
test_and_set_bit() 
-------------------------------------------------------------- 
功能: 
test_and_set_bit - Set a bit and return its old value 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

参数: 
@nr: Bit to set 
@addr: Address to count from 

说明: 
This operation is atomic and cannot be reordered.It also implies a memory barrier. 

static __inline__ int test_and_set_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__ __volatile__( LOCK_PREFIX 
"btsl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 
return oldbit; 

-------------------------------------------------------------- 
__test_and_set_bit() 
-------------------------------------------------------------- 
This operation is non-atomic and can be reordered. 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

static __inline__ int __test_and_set_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__( 
"btsl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr)); 
return oldbit; 

-------------------------------------------------------------- 
test_and_clear_bit() 
-------------------------------------------------------------- 
功能: 
test_and_clear_bit - Clear a bit and return its old value 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

参数: 
@nr: Bit to clear 
@addr: Address to count from 

功能: 
This operation is atomic and cannot be reordered.It also implies a memory barrier. 

static __inline__ int test_and_clear_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__ __volatile__( LOCK_PREFIX 
"btrl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 
return oldbit; 

-------------------------------------------------------------- 
__test_and_clear_bit() 
-------------------------------------------------------------- 
说明: 
This operation is non-atomic and can be reordered. 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__( 
"btrl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr)); 
return oldbit; 

-------------------------------------------------------------- 
__test_and_change_bit() 
-------------------------------------------------------------- 
/* WARNING: non atomic and it can be reordered! */ 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

static __inline__ int __test_and_change_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__ __volatile__( 
"btcl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 
return oldbit; 

-------------------------------------------------------------- 
test_and_change_bit() 
-------------------------------------------------------------- 
说明: 
This operation is atomic and cannot be reordered.It also implies a memory barrier. 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

static __inline__ int test_and_change_bit(int nr, volatile void * addr) 

int oldbit; 

__asm__ __volatile__( LOCK_PREFIX 
"btcl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit),"=m" (ADDR) 
:"dIr" (nr) 
: "memory"); 
return oldbit; 

-------------------------------------------------------------- 
constant_test_bit() 
-------------------------------------------------------------- 
static __inline__ int constant_test_bit(int nr, const volatile void * addr) 

return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; 

功能:查看unsigned long型4个字节中第nr位是否为1 
具体的分析可以参考前面的分析。 
-------------------------------------------------------------- 
variable_test_bit() 
-------------------------------------------------------------- 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

功能:在指定的addr[0]中的nr位上,返回该位的值,为0则返回0;为1则返回-1 

static __inline__ int variable_test_bit(int nr, volatile const void * addr) 

int oldbit; 

__asm__ __volatile__( 
"btl %2,%1\n\tsbbl %0,%0" 
:"=r" (oldbit) 
:"m" (ADDR),"dIr" (nr)); 
return oldbit; 

-------------------------------------------------------------- 
test_bit() 
-------------------------------------------------------------- 

#define test_bit(nr,addr) \ 
(__builtin_constant_p(nr) ? \ 
constant_test_bit((nr),(addr)) : \ 
variable_test_bit((nr),(addr))) 

#undef ADDR 

__builtin_constant_p (exp): 
1)函数功能: 
You can use the built-in function __builtin_constant_p to determine if a value is known to be constant at compile-time and hence that GCC can perform constantfolding on expressions involving that value. 

2)参数: 
The argument of the function is the value to test. 
返回 1: if the argument is known to be a compiletime constant 
返回 0 :if it is not known to be a compile-time constant. 
-------------------------------------------------------------- 
set_bit_string() 
-------------------------------------------------------------- 
static inline void set_bit_string(unsigned long *bitmap, unsigned long i,int len) 

unsigned long end = i + len; 
while (i < end) 

__set_bit(i, bitmap); 
i++; 


功能: 
对bitmap所指向的内存单元,从i所指定的位开始,将len个位置1。 

附参考__set_bit()函数: 
static inline void __set_bit(int nr, volatile unsigned long * addr) 

__asm__( 
"btsl %1,%0" 
:"=m" (ADDR) 
:"Ir" (nr)); 

-------------------------------------------------------------- 
__clear_bit_string() 
-------------------------------------------------------------- 

static inline void __clear_bit_string(unsigned long *bitmap, unsigned long i, int len) 

unsigned long end = i + len; 
while (i < end) 

__clear_bit(i, bitmap); 
i++; 


功能: 
对bitmap所指向的内存单元,从i所指定的位开始,将len个位清0。 
附参考__clear_bit()函数: 
static inline void __clear_bit(int nr, volatile unsigned long * addr) 

__asm__ __volatile__( 
"btrl %1,%0" 
:"=m" (ADDR) 
:"Ir" (nr)); 

-------------------------------------------------------------- 
--------------------------------------------------------------- 
原子操作的第二个方面: 逻辑运算,64位平台--全部结束 
**************************************************************** 


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
armlinux学习笔记--LCD驱动程序分析
加锁的各种选择
ubo-header、signature、chunk结构
Linux ELF格式分析
Cortex M3 Bit-BAND
从STM32的位带操作重谈嵌入式中寻址与对齐的理解
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服