linux原子化运行一段代码,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

p

|

|

|----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

--------------------------------------------------------------

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位平台--全部结束

****************************************************************

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值