atomic_inc_not_zero(),atomic_add_unless(), atomic_cmpxchg()
atomic_表示原子操作,不被任何其他操作打扰,一次完成。
------------------------------------------------------------------------------------------------------------
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
//atomic_add_unless(v,a,u)表示如果v的值不等于u就加a,返回1。如果v的值等于u就返回0。
//也就是说,v的值要满足不等于u的条件才加a,否则v的值不变。返回值呢就是加了就返回1,没加就返回0。
static inline int atomic_add_unless(atomic_t *v, int a, int u)
{
int c, old;
c = atomic_read(v);
for (;;) {
if (unlikely(c == (u)))
break;
old = atomic_cmpxchg((v), c, c + (a)); //如果v的值等于c就加a,old=c。如果v的值不等于c,old=v的值;
if (likely(old == c))
break;
c = old;
}
return c != (u);
}
// __raw_cmpxchg(ptr, old, new)表示,如果*ptr等于old, *ptr=new, 返回old;如果*ptr不等于old,*ptr不变,返回*ptr。
//也就是说,ptr的值要满足等于old的条件才改变,不满足条件不改变。返回值始终是原来*ptr的值。
#define __raw_cmpxchg(ptr, old, new, size, lock) \
({ \
__typeof__(*(ptr)) __ret; \
__typeof__(*(ptr)) __old = (old); \
__typeof__(*(ptr)) __new = (new); \
switch (size) { \
case 4: \
asm volatile(lock "cmpxchgl %1,%2" \
: "=a"(__ret) \
: "r"(__new), "m"(*__xg(ptr)), "0"(__old) \
: "memory"); \
break; \
} \
__ret; \
})
//cmpxchgl %src,%dest
//Compares the accumulator (8-32 bits) with "dest". If equal the"dest" is loaded with "src", otherwise the accumulator is loaded with "dest".
------------------------------------------------------------------------------------------------------------
smp_processor_id()
涉及到per_cpu变量定义和访问
------------------------------------------------------------------------------------------------------------
DECLARE_PER_CPU(int, cpu_number);
# define smp_processor_id() raw_smp_processor_id()
#define raw_smp_processor_id() (percpu_read(cpu_number))
#define percpu_read(var) percpu_from_op("mov", per_cpu__##var, \
"m" (per_cpu__##var))
#define percpu_from_op(op, var, constraint) \
({ \
typeof(var) pfo_ret__; \
switch (sizeof(var)) { \
case 4: \
asm(op "l "__percpu_arg(1)",%0" \
: "=r" (pfo_ret__) \
: constraint); \
break; \
} \
pfo_ret__; \
})
其中
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define __percpu_seg fs
#define __percpu_arg(x) "%%"__stringify(__percpu_seg)":%P" #x
//printf("__percpu_arg(x)=%s\n",__percpu_arg(1)); //输出 ("__percpu_arg(x)=%%fs:%P1
//printf("__stringify(__percpu_seg)=%s\n",__stringify(__percpu_seg)); //输出 __stringify(__percpu_seg)=fs
// 最后smp_processor_id()汇编部分展开为
asm("movl %%fs:%P1,%0" //fs: cpu_number表示fs段中cpu_number偏移处
: "=r" (pfo_ret__) //pfo_ret__是输出部%0,q,表示寄存器eax、ebx、ecx或edx中的一个
: "m"(per_cpu__cpu_number));
//“加载全局/中断描述符表”中把__KERNEL_PERCPU段选择子赋给了fs了吗,fs: cpu_number就获得了当前存放在__KERNEL_PERCPU段中cpu_number偏移的内存中
------------------------------------------------------------------------------------------------------------
这里主要是宏的运用
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x) //如果这里直接定义#define __stringify(x...) #x不行,__percpu_seg没法替换成fs
#define __percpu_seg fs
注意__percpu_seg在 __stringify(x...) 里被解析成__stringify_1(fs),再被解析成"fs"。
参考 激活第一个CPU
asmlinkage_protect(n, ret, args...)
防止编译器在调用函数前后改变caller()的寄存器值
------------------------------------------------------------------------------------------------------------
#define asmlinkage_protect(n, ret, args...) \
__asmlinkage_protect##n(ret, ##args)
#define __asmlinkage_protect_n(ret, args...) \
__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args)
#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3))
------------------------------------------------------------------------------------------------------------
例子:
------------------------------------------------------------------------------------------------------------
externintcallee(inta,intb);staticintc;
intcall(inta,intb){ intret=callee(c, b); __asm__("":"=r"(ret):"0"(ret),"g"(a),"g"(b)); //这句没有任何实质操作,却影响编译器行为,强迫把ret,a,b放入栈中。
returnret;
}
//gcc -fomit-frame-pointer -fno-inline -O2 -S tail_call.c
//看它对应的汇编代码
------------------------------------------------------------------------------------------------------------
例子:系统调用sys_open()
------------------------------------------------------------------------------------------------------------
SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(dfd, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(4, ret, dfd, filename, flags, mode);
return ret;
}
//其中asmlinkage_protect展开为__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), "g" (filename), "g" (flags), "g" (mode));
------------------------------------------------------------------------------------------------------------
参考尾部调用优化
宏#和##的区别
一般,宏中用#组合字符串,用##组合变量名。
例子:
------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#define str(n) "str"#n
#define dat(n) dat##n
#define max(x,y) (x)>(y)?(x):(y)
void main()
{
int a = 1;
int b = 2;
int dat(1) = 3;
int c = max(a,b);
printf("str(n)=%s,dat(n)=%d",str(8),dat(1)); //输出 str(n)=str8,dat(n)=3
}
------------------------------------------------------------------------------------------------------------
container_of(ptr, type, member)
ptr是结构类型type的成员名member,container_of(ptr, type, member) 得到地址ptr所在结构体的地址。
------------------------------------------------------------------------------------------------------------
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
------------------------------------------------------------------------------------------------------------
定义一个指针变量__mptr,它的类型是typeof( ((type *)0)->member ) *。
typeof( ((type *)0)->member ),通过结构类型一个成员名称可以得到该成员的数据类型。
offsetof(type,member) ,结构类型和一个成员名称的offsetof,得到该成员离结构头的距离。
smp_mb(),mb(),barrier()
#define barrier() __asm__ __volatile__("": : :"memory")
内存屏障,memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令。
hi~,北风北的猪最后编辑于2011.03.08