Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1017695
  • 博文数量: 297
  • 博客积分: 11721
  • 博客等级: 上将
  • 技术积分: 3431
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-25 10:21
文章分类

全部博文(297)

文章存档

2016年(9)

2011年(71)

2010年(137)

2009年(80)

分类: LINUX

2011-06-29 21:11:33

atomic64_set() 
---------------------------------------------------------------- 
/** 
* atomic64_set - set atomic64 variable 
* @v: pointer to type atomic64_t 
* @i: required value 
* Atomically sets the value of @v to @i. 
*/ 
#define atomic64_set(v,i) (((v)->counter) = (i)) 
---------------------------------------------------------------- 
atomic64_add() 
---------------------------------------------------------------- 
/** 
* atomic64_add - add integer to atomic64 variable 
* @i: integer value to add 
* @v: pointer to type atomic64_t 
* Atomically adds @i to @v. 
*/ 
static __inline__ void atomic64_add(long i, atomic64_t *v) 

__asm__ __volatile__( 
LOCK "addq %1,%0" 
:"=m" (v->counter) 
:"ir" (i), "m" (v->counter)); 

addq:为8个字节的相加。只是指令变化一下。 
类比: 
IBM INTEL汇编指令: 
db: 分配一个字节 
dw: 分配两个字节 
dd: 分配四个字节 
dq: 分配八个字节 
dt: 分配十个字节 
q:表示八个字节 
此处long为8B,atomic64_t中的volatile long数据类型也是8B 

请注意: 
1.在i386上,int: 4B,long: 4B,long long: 8B,ptr: 4B 
2.在ia64上, int: 4B,long: 8B,long long: 8B,ptr: 8B 

具体的可以查看《Linux内核分析及编程》一书,倪继利著 电子工业出版社 P1 
---------------------------------------------------------------- 
atomic64_sub() 
---------------------------------------------------------------- 
/** 
* atomic64_sub - subtract the atomic64 variable 
* @i: integer value to subtract 
* @v: pointer to type atomic64_t 
* Atomically subtracts @i from @v. 
*/ 
static __inline__ void atomic64_sub(long i, atomic64_t *v) 

__asm__ __volatile__( 
LOCK "subq %1,%0" 
:"=m" (v->counter) 
:"ir" (i), "m" (v->counter)); 

1.long ,atomic64_t变量视为8B. 
但自己认为在i386平台上,当用long long才是8B,而long只有4B 
这里当为atomic64_sub(long long i,atomic64_t *v),其中的atomic64_t当为typedef struct{volatile long long counter; }atomic64_t; 
---------------------------------------------------------------- 
atomic64_sub_and_test() 
---------------------------------------------------------------- 
/** 
* atomic64_sub_and_test - subtract value from variable and test result 
* @i: integer value to subtract 
* @v: pointer to type atomic64_t 
* Atomically subtracts @i from @v and returns 
* true if the result is zero, or false for all 
* other cases. 
*/ 
static __inline__ int atomic64_sub_and_test(long i, atomic64_t *v) 

unsigned char c; 

__asm__ __volatile__( 
LOCK "subq %2,%0; sete %1" 
:"=m" (v->counter), "=qm" (c) 
:"ir" (i), "m" (v->counter) 
: "memory"); 
return c; 

---------------------------------------------------------------- 
atomic64_inc() 
---------------------------------------------------------------- 
/** 
* atomic64_inc - increment atomic64 variable 
* @v: pointer to type atomic64_t 
* Atomically increments @v by 1. 
*/ 
static __inline__ void atomic64_inc(atomic64_t *v) 

__asm__ __volatile__( 
LOCK "incq %0" 
:"=m" (v->counter) 
:"m" (v->counter)); 

---------------------------------------------------------------- 
atomic64_dec() 
---------------------------------------------------------------- 
/** 
* atomic64_dec - decrement atomic64 variable 
* @v: pointer to type atomic64_t 
* Atomically decrements @v by 1. 
*/ 
static __inline__ void atomic64_dec(atomic64_t *v) 

__asm__ __volatile__( 
LOCK "decq %0" 
:"=m" (v->counter) 
:"m" (v->counter)); 

---------------------------------------------------------------- 
atomic64_dec_and_test() 
---------------------------------------------------------------- 
/** 
* atomic64_dec_and_test - decrement and test 
* @v: pointer to type atomic64_t 
* Atomically decrements @v by 1 and 
* returns true if the result is 0, or false for all other 
* cases. 
*/ 
static __inline__ int atomic64_dec_and_test(atomic64_t *v) 

unsigned char c; 

__asm__ __volatile__( 
LOCK "decq %0; sete %1" 
:"=m" (v->counter), "=qm" (c) 
:"m" (v->counter) 
: "memory"); 
return c != 0; 

v->counter逐步减一 
returns 1 if the v->counter == 0 
return 0 if the v->counter != 0 

---------------------------------------------------------------- 
atomic64_inc_and_test() 
---------------------------------------------------------------- 
/** 
* atomic64_inc_and_test - increment and test 
* @v: pointer to type atomic64_t 
* Atomically increments @v by 1 
* and returns true if the result is zero, or false for all 
* other cases. 
*/ 
static __inline__ int atomic64_inc_and_test(atomic64_t *v) 

unsigned char c; 

__asm__ __volatile__( 
LOCK "incq %0; sete %1" 
:"=m" (v->counter), "=qm" (c) 
:"m" (v->counter) 
: "memory"); 
return c != 0; 

v->counter逐步加一 
returns 1 if the v->counter == 0 
return 0 if the v->counter != 0 
---------------------------------------------------------------- 
atomic64_add_negative() 
---------------------------------------------------------------- 
/** 
* atomic64_add_negative - add and test if negative 
* @v: pointer to atomic64_t 
* @i: integer value to add 
* Atomically adds @i to @v and returns true 
* if the result is negative, or false when 
* result is greater than or equal to zero. 
*/ 
static __inline__ long atomic64_add_negative(long i, atomic64_t *v) 

unsigned char c; 

__asm__ __volatile__( 
LOCK "addq %2,%0; sets %1" 
:"=m" (v->counter), "=qm" (c) 
:"ir" (i), "m" (v->counter) 
: "memory"); 
return c; 

解释如下: 
v->counter = v->counter + i; 
if (v->counter < 0) 

c = 1; 
return 1; 

if (v->counter >= 0) 

c = 0; 
return 0; 

---------------------------------------------------------------- 
atomic_add_return() 
---------------------------------------------------------------- 
/** 
* atomic_add_return - add and return 
* @v: pointer of type atomic_t 
* @i: integer value to add 
* Atomically adds @i to @v and returns @i + @v 
*/ 
static __inline__ int atomic_add_return(int i, atomic_t *v) 

int __i = i; 
__asm__ __volatile__( 
LOCK "xaddl %0, %1;" 
:"=r"(i) 
:"m"(v->counter), "0"(i)); 
return i + __i; 

分析: 
1.%0: i %1: v->counter==> 
xaddl %0,%1; <====> xaddl i,v->counter; 
2.注意这里int也是8B 
用程序解释如下: 
int __i; 
__i = i; 
i = v->counter; 
v->counter = __i + v->counter; 
return v->counter; 
---------------------------------------------------------------- 
atomic_sub_return() 
---------------------------------------------------------------- 

static __inline__ int atomic_sub_return(int i, atomic_t *v) 

return atomic_add_return(-i,v); 

---------------------------------------------------------------- 
atomic_inc_return() atomic_dec_return() 
---------------------------------------------------------------- 

#define atomic_inc_return(v) (atomic_add_return(1,v)) 
#define atomic_dec_return(v) (atomic_sub_return(1,v)) 
---------------------------------------------------------------- 
atomic_clear_mask() 
---------------------------------------------------------------- 
/* These are x86-specific, used by some header files */ 
#define atomic_clear_mask(mask, addr) \ 
__asm__ __volatile__(LOCK "andl %0,%1" \ 
: : "r" (~(mask)),"m" (*addr) : "memory") 
分析: 
1.取出当时在atomic_set_mask()中所设置的掩码mask,将其取反,就将当时其位为1的位置为了0,而当时为0的位为1了。 

2.再通过与运算就将原来在atomic_set_mask()中由mask所人为设置的1位通通变成0了,而无关位不变。 
思路巧妙!!! 

3.用同一个mask,atomic_set_mask()使用该mask将其置位,然后用同一个mask在atomic_clear_mask()将其位清除。 
---------------------------------------------------------------- 
atomic_set_mask() 
---------------------------------------------------------------- 
#define atomic_set_mask(mask, addr) \ 
__asm__ __volatile__(LOCK "orl %0,%1" \ 
: : "r" ((unsigned)mask),"m" (*(addr)) : "memory") 
---------------------------------------------------------------- 
smp_mb__before_atomic_dec() smp_mb__after_atomic_dec() 
smp_mb__before_atomic_inc() smp_mb__after_atomic_inc() 
---------------------------------------------------------------- 
/* Atomic operations are already serializing on x86 */ 
#define smp_mb__before_atomic_dec() barrier() 
#define smp_mb__after_atomic_dec() barrier() 
#define smp_mb__before_atomic_inc() barrier() 
#define smp_mb__after_atomic_inc() barrier() 
---------------------------------------------------------------- 
64位平台的原子操作(算术运算)的头文件至此全部结束 
**************************************************************** 
=============================================================== 
**************************************************************** 
原子操作的第二个方面: 逻辑运算。32位平台和64位平台 
*************************************************************** 
原子操作的第二个方面: 逻辑运算,32位平台 
---------------------------------------------------------------- 
------------------\asm-i386\bitops.h---------------------------- 
---------------------------------------------------------------- 
_I386_BITOPS_H 
---------------------------------------------------------------- 
#ifndef _I386_BITOPS_H 
#define _I386_BITOPS_H 

#include  
#include  
/* 
* These have to be done with inline assembly: that way the *bit-setting is guaranteed to be atomic. All bit operations *return 0 if the bit was cleared before the operation and != 0 *if it was not. 
*bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). 
*/ 
---------------------------------------------------------------- 
LOCK_PREFIX 
---------------------------------------------------------------- 
#ifdef CONFIG_SMP 
#define LOCK_PREFIX "lock ; " 
#else 
#define LOCK_PREFIX "" 
#endif 
为多处理器时,LOCK_PREFIX为锁定总线。 
为单处理器时,LOCK_PREFIX为空操作 
---------------------------------------------------------------- 
ADDR 
---------------------------------------------------------------- 
#define ADDR (*(volatile long *) addr) 

自己认为: 
typedef unsigned long u32; 
#define ADDR (*(volatile u32 *) addr) 
---------------------------------------------------------------- 
内核注释 :原子操作的第二个方面:逻辑运算,32位平台中的各个函数 
---------------------------------------------------------------- 
内核注释: 
1.set_bit - Atomically set a bit in memory 
2. 参数解释: 
@nr: the bit to set 
@addr: the address to start counting from 
3.This function is atomic and may not be reordered. 
这些函数是原子操作且不能被重排序。 
4.See __set_bit() if you do not require the atomic guarantees. 
5.Note: there are no guarantees that this function will not be reordered on non x86 architectures, so if you are writting portable code, make sure not to rely on its reordering guarantees. 
注意:在非x86体系结构上,不能保证函数不被重排序。 
6.Note that @nr may be almost arbitrarily large; this function is not restricted to acting on a single-word quantity. 
nr可以很大,而非只能是单个词的数量的限制。 
---------------------------------------------------------------- 
set_bit() 
---------------------------------------------------------------- 
static inline void set_bit(int nr, volatile unsigned long * addr) 

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

1.先来看intel的相应的指令: 
bt oprd1,oprd2 位测试指令,被测试位送CF 
btc oprd1,oprd2 位测试并取反指令,被测试位送CF并且被测试位取反 
btr oprd1,oprd2 位测试并复位指令,被测试位送CF并且被测试位清0 
bts oprd1,oprd2 位测试并置位指令,被测试位送CF并且被测试位置1 
oprd1:可以是16位或32位通用寄存器和16位或32位存储单元,用于指定要测试的内容。 
oprd2:必须是8位立即数或与操作数oprd1长度相等的通用寄存器,用于指定要测试的位。 
例: bh bl 
mov bx, 4567H==> 0100 0101 0110 0111 
mov ecx,3 
bt bx, cx =====>CF=0 0100 0100 0110 0111 
btc bx, 3 =====>CF=0 0100 0100 1110 0111 
btr bx, cx =====>CF=0 0100 0100 0110 0111 
bts ebx,ecx=====>CF=0 0100 0100 0110 1111 

2.AT&T格式汇编: 
功能一样,只是源操作数,目的操作数顺序相反。 
bt oprd2,oprd1 oprd1为要测试的内容,oprd2为指定要测试的位 
其他指令:btc,btr,bts顺序也同上。 
以上指令改写为: 
btw cx,bx 
btcw 3,bx 
btrw cx,bx 
btsl ecx,ebx 

btsl %1,%0 <=====> btsl nr,ADDR<===> btsl nr,*(volatile u32 *)addr 

3.I:表示立即数 
---------------------------------------------------------------- 
__set_bit() 
---------------------------------------------------------------- 
static inline void __set_bit(int nr, volatile unsigned long * addr) 

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

1.Unlike set_bit(), this function is non-atomic and may be reordered. 
2.无LOCK_PREFIX,不会锁定内存总线,对多处理器而言,不会是原子操作 
---------------------------------------------------------------- 
clear_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能:clear_bit - Clears a bit in memory 
参数:@nr: Bit to clear 
@addr: Address to start counting from 

2.clear_bit() is atomic and may not be reordered. clear_bit()是原子操作,不会被重排序。 

3.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. 
如果是用于多处理器上的锁定目的,当用smp_mb_before_clear_bit()/smp_mb_after_clear_bit()来使数据对其他的处理器也是可见的。 

static inline void clear_bit(int nr, volatile unsigned long * addr) 

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

1.btr oprd1,oprd2 位测试并复位指令,被测试位送CF并且被测试位清0 

2.btrl %1,%0 <==========> btrl nr,ADDR 
%1 == nr: 将要设置的位 
%0 == ADDR: 要被设置的操作数 
---------------------------------------------------------------- 
__clear_bit() 
---------------------------------------------------------------- 
static inline void __clear_bit(int nr, volatile unsigned long * addr) 

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

功能同clear_bit(),只是没有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() 
---------------------------------------------------------------- 
内核注释: 
1.功能:__change_bit - Toggle a bit in memory 
@nr: the bit to change 
@addr: the address to start counting from 

2.Unlike change_bit(), this function is non-atomic and may be reordered.非原子操作,可以被编译器重排序 

3.If it's called on the same region of memory simultaneously, the effect may be that only one operation succeeds. 

static inline void __change_bit(int nr, volatile unsigned long * addr) 

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

1.btc oprd1,oprd2 位测试并取反指令,被测试位送CF并且被测试位取反 

2.btcl %1,%0 <==========> btcl nr,ADDR 
%1 == nr: 将要设置的位 
%0 == ADDR: 要被设置的操作数 
---------------------------------------------------------------- 
change_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能:change_bit - Toggle a bit in memory 
参数:@nr: Bit to change 
@addr: Address to start counting from 

2. change_bit() is atomic and may not be reordered. It may be reordered on other architectures than x86. 
不能被重排序,是原子操作,但在非x86体系结构上可能会被编译器秩序重排序。 

3.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 unsigned long * addr) 

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

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

2.参数:@nr: Bit to set 
@addr: Address to count from 
3.This operation is atomic and cannot be reordered.It may be reordered on other architectures than x86.It also implies a memory barrier. 

static inline int test_and_set_bit(int nr, volatile unsigned long * addr) 

int oldbit; 

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

分析: 
1.汇编指令bts,btsl: 
(Intel指令) bts oprd1,oprd2 位测试并置位指令,被测试位送CF并且被测试位置1 

(AT&T指令) btsl %2,%1 <=====> btsl nr,ADDR 
将ADDR操作数中的第nr位(从0始)送CF,并且第nr位置1。 

2.汇编指令sbb,sbbl: 
(Intel指令) sbb oprd1,oprd2 
oprd1 <-- oprd1 - oprd2 - CF 

(AT&T指令) sbbl oprd1,oprd2 
oprd2 <-- oprd2 - oprd1 - CF 

3.sbbl %0,%0 <===> sbbl oldbit,oldbit 
即为:oldbit <-- oldbit - oldbit - CF ===> oldbit <-- -CF 
而CF为标志位,只有一位,为0或为1, 
若第nr位为0,则送往CF中的为0,经过sbbl后,oldbit == -0 == 0 
若第nr位为1,则送往CF中的为1,经过sbbl后,oldbit == -1 == 1的反码(为1111..110)加一,变成了111...111 
综上所述:oldbit里面保存的是操作数中第nr位的原始值0或为-1。 
所以本test_and_set_bit()函数的功能是将操作数ADDR中的第nr位设置为1(用btsl指令实现),用oldbit保存该位的原始值(用sbbl %0,%0来实现)并返回之。 
注意: 
"btsl %2,%1\n\tsbbl %0,%0" 
返回值: 
当该位为0的时候,返回0 
当该位为1的时候,返回-1 
认为源代码中有误,并非为1的时候去返回1,而是返回了-1。 
作者将源函数为主要函数,自己再编写一个主函数来调用它,经调试认为的确是源代码中有误。 
---------------------------------------------------------------- 
__test_and_set_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能: 
__test_and_set_bit - Set a bit and return its old value 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 
2.参数: 
@nr: Bit to set 
@addr: Address to count from 

3.This operation is non-atomic and can be reordered. 
非原子操作,可以被重排序。 

4.If two examples of this operation race, one can appear to succeed but actually fail. You must protect multiple accesses with a lock. 

static inline int __test_and_set_bit(int nr, volatile unsigned long * addr) 

int oldbit; 

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

类似test_and_set_bit(),只是没有LOCK_PREFIX而矣。 
---------------------------------------------------------------- 
test_and_clear_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能: 
test_and_clear_bit - Clear a bit and return its old value 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 
2.参数: 
@nr: Bit to clear 
@addr: Address to count from 

3.This operation is atomic and cannot be reordered.It can be reorderdered on other architectures other than x86.It also implies a memory barrier. 

static inline int test_and_clear_bit(int nr, volatile unsigned long * addr) 

int oldbit; 

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

分析类似上面,只是这里为btrl指令。 
btr oprd1,oprd2 位测试并复位指令,被测试位送CF并且被测试位清0 
---------------------------------------------------------------- 
__test_and_clear_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能: 
__test_and_clear_bit - Clear a bit and return its old value 
注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 
2.参数: 
@nr: Bit to clear 
@addr: Address to count from 

3.This operation is non-atomic and can be reordered. 

4.If two examples of this operation race, one can appear to succeed but actually fail. You must protect multiple accesses with a lock. 

static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) 

int oldbit; 

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

分析类似上面。 
---------------------------------------------------------------- 
__test_and_change_bit() 
---------------------------------------------------------------- 
/* WARNING: non atomic and it can be reordered! */ 
虽有"memory",只是说明CPU寄存器中的数据可能与内存单元中的数据不一致,由于没有LOCK_PREFIX,故不能保证其原子操作,且可能会被编译器重排序。 

注意返回值:当指定的该位为0时,返回0;当指定的该位为1时,返回-1,源代码有误,作者在计算机上设计一个主函数调用之,证实了这一想法。 

static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) 

int oldbit; 

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

分析: 
btc oprd1,oprd2 位测试并取反指令,被测试位送CF并且被测试位取反 
btcl %2,%1 为操作数%1的第%2位送CF,然后将%1操作数的第%2位取反。 
在sbbl %0,%0作用下,将操作数%1的第%2位的原始值返回之。 
---------------------------------------------------------------- 
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 unsigned long* addr) 

int oldbit; 

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

---------------------------------------------------------------- 
test_bit() 
---------------------------------------------------------------- 
1.Fool kernel-doc since 
2.#if 0,条件永不满足。 
3.怎么回事,现在还没弄明白。??? 
#if 0 /* Fool kernel-doc since it doesn't do macros yet */ 
/** 
* test_bit - Determine whether a bit is set 
* @nr: bit number to test 
* @addr: Address to start counting from 
*/ 
static int test_bit(int nr, const volatile void * addr); 
#endif 
---------------------------------------------------------------- 
constant_test_bit() 
---------------------------------------------------------------- 
注意:nr虽为int型变量,但这里nr应当为一个常量,与下面的函数进行对照。因为这里nr为常量,所以这里就用一句C语言来实现,而variable_test_bit()中的nr为int型变量,就用汇编指令去实现了。 

功能:查看unsigned long型4个字节中第nr位是否为1 

static inline int constant_test_bit(int nr, const volatile unsigned long *addr) 

return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; 

精彩!!! 
我的想法: 
return ((1UL << (nr & 31)) & addr[0]) != 0; 

分析: 
1.const volatile unsigned long * addr 表示addr所指向的unsigned long型地址单元中的数据不能被更改。 
具体参考:Linux Kernel内核字节序源代码分析-swab.h和big_endian.h一份资料。 

2.代码分析: 
addr 为unsigned long *型,故为4B,计32位 

nr为int型整数,nr取值范围可以很大,但由于addr只有4B,计32位,所以只有当0 <= nr <= 31 nr的数值才是有效的。 

31这个十进制数化为二进制后,为11111,计5位二进制数 

nr & 31就是将4B的int型整数,将高于第4位(从第0位起,不含第4位)全部清了0,这样就使大于31的整数nr转化到小于或等于31。也即:只保留了低5位二进制位。本质就是使nr <= 31。 

1UL << (nr & 31) :当nr在[0,31]区间时候,将unsigned long型4B的00000000 00000000 00000000 00000001中的1移动到第nr位(从0始-->31位止),其余位为0。 

nr >> 5:即将低5位,表示数的范围为0-31,用来描述这4B,32位(0-31)的的部分清0。 

若 nr在[0,31]区间内,则(nr >> 5)后的结果为0,则addr[nr >> 5]<==>addr[0],即取出unsigned long addr[0]这个字节,然后在(1UL << (nr & 31)) & (addr[nr >> 5])来看这4个字节中第nr位是否为1。这是最常见的情况。 

若nr > 31,则(nr >> 5)后其低5位(第0位-->第4位)被清0,留下第5位-->第31位的数据,并且全部右移了5位。这种情况发生于nr超出于32这个数字 大小。然后addr[nr >> 5]则是取出(nr >> 5)这个数组下标的unsigned long 型的4B数据去参于与运算。比如:当nr == [32,63]中的一个数值时候,nr >> 5得到(nr >> 5)为1,则addr[nr >> 5]<==>addr[1];当nr == [64,95]中的一个数值时候,nr >> 5得到(nr >> 5)为2,则addr[nr >> 5]<==>addr[2],相当于nr/32,以32为单位换算成unsigned long 型数组的数组元素下标。然后取出这个unsigned long 型数组元素,共4B,来看第(nr & 31)位是否为1。但这种情况后果不定,应当尽量避免。 
---------------------------------------------------------------- 
variable_test_bit() 
---------------------------------------------------------------- 
这里的nr为一个变量: 
static inline int variable_test_bit(int nr, const volatile unsigned long * addr) 

int oldbit; 

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

分析: 
bt oprd1,oprd2 位测试指令,被测试位送CF 
---------------------------------------------------------------- 
test_bit() 
---------------------------------------------------------------- 

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

分析: 
1.代码分析: 
如果nr为一个常量,__builtin_constant_p(nr)返回1,则调用constant_test_bit() 
如果nr为一个变量,__builtin_constant_p(nr)返回0,则调用variable_test_bit() 

2.(参考gcc手册) 
__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. 

3)使用场合: 
You would typically use this function in an embedded application where memory was a critical resource. If you have some complex calculation, you may want it to be folded if it involves constants, but need to call a function if it does not. 
用于内存资源为关键资源的地方和复合运算的地方。 
For example: 
#define Scale_Value(X) \ 
(__builtin_constant_p (X) \ 
? ((X) * SCALE + OFFSET) : Scale (X)) 

4)宏和内联函数都可以用: 
You may use this built-in function in either a macro or an inline function. 

5)初始化中的应用: 
You may also use __builtin_constant_p in initializers for static data. 
For instance,you can write 
static const int table[] = 

__builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1, 
/* . . . */ 
}; 
This is an acceptable initializer even if EXPRESSION is not a constant expression.GCC must be more conservative about evaluating the built-in in this case, because it has no opportunity to perform optimization. 
Previous versions of GCC did not accept this built-in in data initializers. The earliest version where it is completely safe is 3.0.1. 
---------------------------------------------------------------- 
find_first_zero_bit() 
---------------------------------------------------------------- 
内核注释: 
1.功能: 
find_first_zero_bit - find the first zero bit in a memory region 
找到第一个为0的位,并将该位的序号返回 

2.参数: 
@addr: The address to start the search at 
@size: The maximum size to search,以位为单位 

3.返回值: 
Returns the bit-number of the first zero bit, not the number of the byte containing a bit. 

分析前的准备工作: 
1.汇编指令: 
1)repe: 
repe:要单独写一行,不要和它修饰的指令(如:movsb,stosb)写在同一行。 
repe是重复前缀。当两串相等,即ZF=1时,则结束循环。它可以使串指令反复执行(CX不等于0 就执行),每执行一次,CX的内容减1。 

2)scasl: 
scasl: 
intel汇编指令:scansd ; 
串扫描指令,以双字为单位,scansd指令使用eax, 
在DF=0时,每次执行操作后相应指针加4 
在DF=1时,每次执行操作后相应指针减4 
若只使用16位指针,则串中源指针为DS:SI,目的指针为:ES:DI 
若只使用32位指针,则串中源指针为DS:ESI,目的指针为:ES:EDI 
此外,可以通过段前缀超越改变源串使用的段寄存器,但不能改变目的串的段寄存器。 
功能:以ES:EDI/DI指向目标串的首地址,每执行一次则扫描目标串的一个双字是否与EAX中的内容相等,如果相等则置ZF=1,否则置ZF=0.每扫描一次EDI都要按DF的值自动加4或减4. 
若加上前缀REPZ/REPE,则表示未扫描完(ECX不为零),且由EDI所指的串元素与EAX的值相等(ZF=1)则继续扫描;如果ZF=0或ECX=0则停止扫描 
AT&T格式汇编指令scasl类似scansd 

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

4)shll: 
先对比intel的汇编指令:shl 
shl oprd,m ; 逻辑左移指令(shift logic left) 
sal oprd,m ; 算术左移指令(shift arithmetic left) 
算术左移和逻辑左移进行相同的动作,但为了方便提供有两个助记符,但要记住,shl和sal本质一样,只有一条机器指令。把操作数oprd左移m位。每移动一位,右边用0补足一位,移出的最高位进入标志位CF。 

AT&T格式汇编:shll m,oprd,本质一样,只是源操作数和目的操作数顺序相反。 

5):AT&T格式: 
a:表示要求使用寄存器eax 

b:表示要求使用寄存器ebx 

c:表示要求使用寄存器ecx 

d:表示要求使用寄存器edx 

D:表示要求使用寄存器edi 

&: 在一般情况下,gcc将把输出操作数和一个不相干的输入操作数分配在同一个寄存器中,因为它假设在输出产生之前所有的输入都补消耗掉了。但是如果汇编程序 中有多条汇编指令的时候,这种假设就不再正确。这时,在输出操作数前加上一个“&”就可以保证输出操作数不会覆盖输入,即:gcc将为此输出操作 数分配一个输入操作数还没有使用的寄存器。 

%0,%1...:表示使用寄存器的样板操作数,而对寄存器则使用双%%以示区分。 

\n:在汇编指令中插入换行 
\t:在汇编指令中插入TAB 

-4(%ebp): [ebp-4] 
-4(%%edi):edi-4 

1f:向前跳 
1b:向后跳 

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

int d0, d1, d2; 
int res; 

if (!size) return 0; 
/* This looks at memory. Mark it volatile to tell gcc not to move it around */ 
__asm__ __volatile__( 
"movl $-1,%%eax\n\t" 
"xorl %%edx,%%edx\n\t" 
"repe; scasl\n\t" 
"je 1f\n\t" 
"xorl -4(%%edi),%%eax\n\t" 
"subl $4,%%edi\n\t" 
"bsfl %%eax,%%edx\n" 
"1:\tsubl %%ebx,%%edi\n\t" 
"shll $3,%%edi\n\t" 
"addl %%edi,%%edx" 
:"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) 
:"1" ((size + 31) >> 5), "2" (addr), "b" (addr) 
: "memory"); 
return res; 

正式分析: 
1.源代码分析: 

初始化情况: 
"1" ((size + 31) >> 5):size: The maximum size to search,而%1是指"=&c" (d0),即为ECX计数器,也就是将最在搜索长度size(以位计算)按4B,32位换算一下有多少个双字。 

"2" (addr):"2"是指"=&D" (d1),也即EDI, 

"b" (addr):EBX中存放参数addr(要开始搜索的地址,也就是该字符串起始地址) 

指令执行分析: 
1)movl %-1,%%eax; :将eax = -1,而-1的补码为11111111 11111111 11111111 11111111,所以eax的内容就为这32个1 
movl $-1 ,%%eax;这里用movl $0xffffffffh,%%eax;也行,但这里用$-1有其高明之处,对于$-1来说,无论%%eax是32位,还是将来扩充到64位,或是变成其 它多少位,均能使其全部填充滿'1';而对$0xffffffffh,则%%eax必须是32位,一旦位数有变将又得相应改变$0xff...fh的个数 或数值。仔细思考,发现这个地方虽似简单,实则是Linux内核开发人员经过认真设计了的,十分巧妙,佩服!!! 

2)xorl %%edx,%%edx; :edx清0 

3)repe; scasl; :以ES:EDI指向目标串的首地址,每执行一次则扫描目标串的一个双字是否与EAX中的内容相等,如果相等则置ZF=1,否则置ZF=0.每扫描一次 EDI都要按DF的值自动加4或减4.由于这里加上了前缀REPE,则表示如果未扫描完(ECX不为零),且由EDI所指的串元素与EAX的值相等 (ZF=1)则继续扫描,即:循环条件为ECX != 0 && ZF == 1;如果ZF == 0(由EDI所指的串元素与EAX的值不相等) 或 ECX == 0(已经扫描完)则停止扫描. 

4)je 1f; :当跳出循环时候,若为ECX == 0,已经扫描完这种情况则此时ZF == 1,je条件满足,要跳转;若为ZF == 0(由EDI所指的串元素与EAX的值不相等)这种情况时候,则je条件不满足,不会跳转。 
由 于EAX 为32个二进制的‘1’,在repe; scasl;作用下,每次比较ES:EDI所扫描目标串的一个双字是否与EAX中的内容相等,即扫描ES:EDI目标串的一个双字是否为全'1',是全 '1',不跳,EDI <=== EDI +/- 4,再比较下一个双字是否为全'1',直到ECX == 0,全比较完了或找到一个双字中含有一个或多个'0'时,repe循环结束。 

5)xorl -4(%%edi),%%eax; :当执行这条语句时候,je没有跳转,即:ZF == 0(由EDI所指的串元素与EAX的值不相等),也就是找到了一个双字,其中含有一个或多个'0'。由于%%eax全1,通过xorl运算,将eax中的 为1的位全部清0,而在[edi - 4]中原来为0的位(也就是我们要找的)就使得%%eax中的对应的位的'1'不变,这个变换结果存放在%%eax中。注意:该双字中含有一个或多个 '0'的位所对应的%%eax中的位上的'1'都不动,维持'1'不变,而该双字中原来为'1'的位就与%%eax中的相应为'1'的位转化为了'0', 结果要找寻[edi-4]中的第一个为'0'的位的任务就转化为找寻%%eax中的第一个为'1'的位。 

6)subl $4,%%edi; :由于%edi每次比较后都会+/-(这里是+)4B,故找到含'0'的双字后,跳出循环前还会+4,所以这里要将%%edi的值减去个4。 

7)bsfl %%eax,%%edx; :从右向左(从位0-->位15或位31)扫描双字操作数%%eax中第一个含"1"的位,并把扫描到的第一个含'1'的位的位号送%%edx中保 存。(EDX中保存了在ES:EDI所指向的目标串中的在repe循环中找到的一个含有一个或多个'0'的双字中的第一个含'0'的位置(以位计算),注 意此时已经转化为%%eax中第一个含'1'的位置(以位计算)) 

8)1:\tsubl %%ebx,%%edi; 
情况1: 
当ECX == 0(已经扫描完),则会跳出循环,并且在下一条je 1f;指令下跳到这里。 
此 时,ECX计数器为零,%%edi == %%edi初始值 + 4*ECX的初始计数值,对ES:EDI所指向的字符串,EDI已经指向其字符串的未尾的再后面的4B。由初始化情况知:EBX中存放参数addr(要开 始搜索的地址,也就是该字符串起始地址),则这里subl %%ebx,%%edi;则是%%edi == %%edi - %%edx,也就是指向ES:EDI所指向的字符串的结尾处再后面4个字节的尾指针减去该字符串的首指针,得出该字符串的总长度。该字符串的长度保存 在%%edi中。 

情况2: 
EDI指向已经找到的含'0'的双字处,(edi在前面的指令中已经减了4,调回来指向这个含 '0'的双字处)。由初始化情况知:EBX中存放参数addr(要开始搜索的地址,也就是该字符串起始地址),则这里subl %%ebx,%%edi;则是%%edi == %%edi - %%edx,也就是指向已经找到的含'0'的双字处的指针减去该字符串的首指针,得出该所找到的含'0'的双字处距离该字符串的首部的长度,这是个距离。 这个距离保存在%%edi中。 
以上的字符串的长度和所找到的含'0'的双字处距离该字符串的首部的长度,这个距离均是EDI和EBX地址之差,而计算机中的地址是以字节为单位的,故这里的长度与距离均是指多少个字节。 

9)shll $3,%%edi; : 
若%%edi为该字符串的总长度(以字节计),这里将该字节数转换为位,edi = edi * 8,即:%%edi为该字符串的总长度(以位计). 

若%%edi为距离(以字节计),也将该字节数转换为位,edi = edi * 8。 
即%%edi为前面的不含'0'的双字的总位数。或字符串的总长度的总位数。 
%%edi为前面的不含'0'的双字的总位数. 

10)addl %%edi,%%edx;: 
若是%%edi为该字符串的总长度(以位计),则%%edx在前面被清零了, 
若 是%%edi为前面的不含'0'的双字的总位数,则%%edx为保存了在ES:EDI所指向的目标串中的在repe循环中找到的一个含有一个或多个'0' 的双字中的第一个含'0'的位置(以位计算)。此时,将前面的不含'0'的双字的总位数加在这个在所找到的含'0'的双字中的第一个含'0'的位置,就得 出了在ES:EDI指向目标串中的第一个含'0'的位置(以位计算)。 

最后,"=d" (res)将edx中所保存的最终结果保存在res中,由函数返回。 

3.返回值: 
1.若size == 0,则返回0 
2.若字符串中不含'0',则返回整个字符串的长度(以位计),也就是size的以位计的数值 
3.若找到了含'0'的位,返回其在整个字符串中的位置。 

改写成C语言: 

算法: 
初始条件: 
const unsigned long *addr:要比较的字符串的首地址 
unsigned int size:要比较的最大长度,以位计(与汇编指令保持一致) 
ebx = addr ; //ebx表示所扫描字符串的首地址 

说明:本C语言程序纯粹只是为了解说以上这个汇编程序的,所以变量名尽可能与以上寄存器名相似,以达到解说目的。 

Step 1: 令eax = 0xffffffffh ,作为要比较的数 (eax为C语言的变量,unsigned long) 
edx = edx ^ edx ,edx清零 (edx为C语言的变量,unsigned long) 
edx主要用于在所找到的含'0'的双字中,'0'所在的位。如果是没找到,则是用于保存该字符串的总长度(用位来表示) 

Step 2: size :表示最多要搜索的位数,unsigned int类型; 
ecx = ((size + 31) >> 5) ,ecx位计数器换算成为字节计数器 (ecx为C语言的变量) 

Step 3: edi为unsigned long *型指针 
edi = addr; 
while( *(edi) == eax && ecx != 0) 

ecx -= 4; //每次比较4B 
edi++; 

edi++; //这里是为了和汇编指令repe; scasl;一样,模拟其的运行 
//如果是没有找到没找到含'0'的字符串,则edi将指向addr所指向字符串的尾部的再后面4个字节 
//如果是找到了含'0'的字符串,则edi将指向所找到的含'0'的字符串的下4B的开始 

Step 4: 
if(ecx == 0) //没找到含'0'的字符串 
goto nofound; //这里使用goto是为了和汇编程序一至 

Step 5: //找到了含'0'的字符串 
eax = *(edi - 4) ^ eax;//调回edi指向含'0'的字符串的这个双字处,利用其中为0的位置eax中相应的位为1;而*(edi -4)中为1的位, 
//则eax中相应的位置为0。于是找寻*(edi - 4)中第一个为0的位的问题转化为找寻eax中第一个为1的位 
edi -= 4; 

Step 6: 
//从右向左(从位0-->位15或位31)扫描eax中第一个含"1"的位,结果保存在edx中 
scan_bit_rest_num = 0; 
while( ((eax & (u32)1) == 0) && (scan_bit_rest_num <32) ) 

eax >>= 1; 
scan_bit_rest_num++; 

if( scan_bit_rest_num < 32 )//找到了第一个含"1"的位 
edx = scan_bit_rest_num;//保存了addr所指向的字符串中含'0'的双字中的第一个含'0'的位置,以位计。 
//即:也就是对应的的eax中第一个含"1"的位置,以位计。 


nofound: 
edi = edi - ebx;//如果是没找到含'0'的双字:得出该字符串的总长度,以字节计 
//如果是找到了含'0'的双字:为含'0'的双字处距离该字符串的首部的长度,以字节计. 
edi <<= 3; //将字节数换算成位数计 
edx += edi; 

C程序: 
const unsigned long *addr; 
unsigned long eax; 
unsigned long edx; 
unsigned long ecx; 
unsigned long *edi; 
unsigned long scan_bit_rest_num; 
unsigned long *ebx;//ebx表示所扫描字符串的首地址 

eax = 0xffffffffh; 
edx = edx ^ edx; 
ecx = ((size + 31) >> 5); 
edi = addr; 
ebx = addr ; 

while( *(edi) == eax && ecx != 0) 

ecx -= 4; //每次比较4B 
edi++; 

edi++; 
if(ecx == 0) //没找到含'0'的字符串 
goto nofound; //这里使用goto是为了和汇编程序一至 

//找到了含'0'的字符串 
eax = *(edi - 4) ^ eax; 
edi -= 4; 
scan_bit_rest_num = 0; 
while( ((eax & (u32)1) == 0) && (scan_bit_rest_num <32) ) 

eax >>= 1; 
scan_bit_rest_num++; 

if( scan_bit_rest_num < 32 )//找到了第一个含"1"的位 
edx = scan_bit_rest_num; 

nofound: 
edi = edi - ebx; 
edi <<= 3; //将字节数换算成位数计 
edx += edi; 
return(edx); 
阅读(2471) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~