linux内核部件分析(二)——原子性操作atomic

在任何处理器平台下,都会有一些原子性操作,供操作系统使用,我们这里只讲x86下面的。在单处理器情况下,每条指令的执行都是原子性的,但在多处理器情况下,只有那些单独的读操作或写操作才是原子性的。为了弥补这一缺点,x86提供了附加的lock前缀,使带lock前缀的读修改写指令也能原子性执行。带lock前缀的指令在操作时会锁住总线,使自身的执行即使在多处理器间也是原子性执行的。xchg指令不带lock前缀也是原子性执行,也就是说xchg执行时默认会锁内存总线。原子性操作是线程间同步的基础,linux专门定义了一种只进行原子操作的类型atomic_t,并提供相关的原子读写调用API。本节就来分析这些原子操作在x86下的实现。

[cpp]view plaincopyprint?

    typedefstruct{volatileintcounter;}atomic_t;

原子类型其实是int类型,只是禁止寄存器对其暂存。

[cpp]view plaincopyprint?

    #defineATOMIC_INIT(i){(i)}

原子类型的初始化。32位x86平台下atomic API在arch/x86/include/asm/atomic_32.h中实现。

[cpp]view plaincopyprint?

    staticinlineintatomic_read(constatomic_t*v){returnv->counter;}staticinlinevoidatomic_set(atomic_t*v,inti){v->counter=i;}

单独的读操作或者写操作,在x86下都是原子性的。

[cpp]view plaincopyprint?

    staticinlinevoidatomic_add(inti,atomic_t*v){asmvolatile(LOCK_PREFIX"addl%1,%0":"+m"(v->counter):"ir"(i));}staticinlinevoidatomic_sub(inti,atomic_t*v){asmvolatile(LOCK_PREFIX"subl%1,%0":"+m"(v->counter):"ir"(i));}

atomic_add和atomic_sub属于读修改写操作,实现时需要加lock前缀。

[cpp]view plaincopyprint?

    staticinlineintatomic_sub_and_test(inti,atomic_t*v){unsignedcharc;asmvolatile(LOCK_PREFIX"subl%2,%0;sete%1":"+m"(v->counter),"=qm"(c):"ir"(i):"memory");returnc;}

atomic_sub_and_test执行完减操作后检查结果是否为0。

[cpp]view plaincopyprint?

    staticinlinevoidatomic_inc(atomic_t*v){asmvolatile(LOCK_PREFIX"incl%0":"+m"(v->counter));}staticinlinevoidatomic_dec(atomic_t*v){asmvolatile(LOCK_PREFIX"decl%0":"+m"(v->counter));}

atomic_inc和atomic_dec是递增递减操作。

[cpp]view plaincopyprint?

    staticinlineintatomic_dec_and_test(atomic_t*v){unsignedcharc;asmvolatile(LOCK_PREFIX"decl%0;sete%1":"+m"(v->counter),"=qm"(c)::"memory");returnc!=0;}

atomic_dec_and_test在递减后检查结果是否为0。

[cpp]view plaincopyprint?

    staticinlineintatomic_inc_and_test(atomic_t*v){unsignedcharc;asmvolatile(LOCK_PREFIX"incl%0;sete%1":"+m"(v->counter),"=qm"(c)::"memory");returnc!=0;}

atomic_inc_and_test在递增后检查结果是否为0。

[cpp]view plaincopyprint?

    staticinlineintatomic_add_negative(inti,atomic_t*v){unsignedcharc;asmvolatile(LOCK_PREFIX"addl%2,%0;sets%1":"+m"(v->counter),"=qm"(c):"ir"(i):"memory");returnc;}

atomic_add_negative在加操作后检查结果是否为负数。

[cpp]view plaincopyprint?

    staticinlineintatomic_add_return(inti,atomic_t*v){int__i;#ifdefCONFIG_M386unsignedlongflags;if(unlikely(boot_cpu_data.x86<=3))gotono_xadd;#endif/*Modern486+processor*/__i=i;asmvolatile(LOCK_PREFIX"xaddl%0,%1":"+r"(i),"+m"(v->counter)::"memory");returni+__i;#ifdefCONFIG_M386no_xadd:/*Legacy386processor*/local_irq_save(flags);__i=atomic_read(v);atomic_set(v,i+__i);local_irq_restore(flags);returni+__i;#endif}

atomic_add_return 不仅执行加操作,而且把相加的结果返回。它是通过xadd这一指令实现的。

[cpp]view plaincopyprint?

    staticinlineintatomic_sub_return(inti,atomic_t*v){returnatomic_add_return(-i,v);}

atomic_sub_return 不仅执行减操作,而且把相减的结果返回。它是通过atomic_add_return实现的。

[cpp]view plaincopyprint?

    staticinlineintatomic_cmpxchg(atomic_t*v,intold,intnew){returncmpxchg(&v->counter,old,new);}#definecmpxchg(ptr,o,n)\((__typeof__(*(ptr)))__cmpxchg((ptr),(unsignedlong)(o),\(unsignedlong)(n),\sizeof(*(ptr))))staticinlineunsignedlong__cmpxchg(volatilevoid*ptr,unsignedlongold,unsignedlongnew,intsize){unsignedlongprev;switch(size){case1:asmvolatile(LOCK_PREFIX"cmpxchgb%b1,%2":"=a"(prev):"q"(new),"m"(*__xg(ptr)),"0"(old):"memory");returnprev;case2:asmvolatile(LOCK_PREFIX"cmpxchgw%w1,%2":"=a"(prev):"r"(new),"m"(*__xg(ptr)),"0"(old):"memory");returnprev;case4:asmvolatile(LOCK_PREFIX"cmpxchgl%k1,%2":"=a"(prev):"r"(new),"m"(*__xg(ptr)),"0"(old):"memory");returnprev;case8:asmvolatile(LOCK_PREFIX"cmpxchgq%1,%2":"=a"(prev):"r"(new),"m"(*__xg(ptr)),"0"(old):"memory");returnprev;}returnold;}

atomic_cmpxchg是由cmpxchg指令完成的。它把旧值同atomic_t类型的值相比较,如果相同,就把新值存入atomic_t类型的值中,返回atomic_t类型变量中原有的值。

[cpp]view plaincopyprint?

    staticinlineintatomic_xchg(atomic_t*v,intnew){returnxchg(&v->counter,new);}#definexchg(ptr,v)\((__typeof__(*(ptr)))__xchg((unsignedlong)(v),(ptr),sizeof(*(ptr))))staticinlineunsignedlong__xchg(unsignedlongx,volatilevoid*ptr,intsize){switch(size){case1:asmvolatile("xchgb%b0,%1":"=q"(x):"m"(*__xg(ptr)),"0"(x):"memory");break;case2:asmvolatile("xchgw%w0,%1":"=r"(x):"m"(*__xg(ptr)),"0"(x):"memory");break;case4:asmvolatile("xchgl%k0,%1":"=r"(x):"m"(*__xg(ptr)),"0"(x):"memory");break;case8:asmvolatile("xchgq%0,%1":"=r"(x):"m"(*__xg(ptr)),"0"(x):"memory");break;}returnx;}

atomic_xchg则是将新值存入atomic_t类型的变量,并将变量的旧值返回。它使用xchg指令实现。

[cpp]view plaincopyprint?

    /***atomic_add_unless-addunlessthenumberisalreadyagivenvalue*@v:pointeroftypeatomic_t*@a:theamounttoaddtov…*@u:…unlessvisequaltou.**Atomicallyadds@ato@v,solongas@vwasnotalready@u.*Returnsnon-zeroif@vwasnot@u,andzerootherwise.*/staticinlineintatomic_add_unless(atomic_t*v,inta,intu){intc,old;c=atomic_read(v);for(;;){if(unlikely(c==(u)))break;old=atomic_cmpxchg((v),c,c+(a));if(likely(old==c))break;c=old;}returnc!=(u);}

atomic_add_unless的功能比较特殊。它检查v是否等于u,如果不是则把v的值加上a,返回值表示相加前v是否等于u。因为在atomic_read和atomic_cmpxchg中间可能有其它的写操作,所以要循环检查自己的值是否被写进去。

[cpp]view plaincopyprint?

    #defineatomic_inc_not_zero(v)atomic_add_unless((v),1,0)#defineatomic_inc_return(v)(atomic_add_return(1,v))#defineatomic_dec_return(v)(atomic_sub_return(1,v))

atomic_inc_not_zero在v值不是0时加1。

atomic_inc_return对v值加1,并返回相加结果。

atomic_dec_return对v值减1,并返回相减结果。

[cpp]view plaincopyprint?

    #defineatomic_clear_mask(mask,addr)\asmvolatile(LOCK_PREFIX"andl%0,%1"\::"r"(~(mask)),"m"(*(addr)):"memory")

atomic_clear_mask清除变量某些位。

[cpp]view plaincopyprint?

    #defineatomic_set_mask(mask,addr)\asmvolatile(LOCK_PREFIX"orl%0,%1"\::"r"(mask),"m"(*(addr)):"memory")

atomic_set_mask将变量的某些位置位。

[cpp]view plaincopyprint?

    /*Atomicoperationsarealreadyserializingonx86*/#definesmp_mb__before_atomic_dec()barrier()#definesmp_mb__after_atomic_dec()barrier()#definesmp_mb__before_atomic_inc()barrier()#definesmp_mb__after_atomic_inc()barrier()

因为x86的atomic操作大多使用原子指令或者带lock前缀的指令。带lock前缀的指令执行前会完成之前的读写操作,对于原子操作来说不会受之前 对同一位置的读写操作,所以这里只是用空操作barrier()代替。barrier()的作用相当于告诉编译器这里有一个内存屏障,放弃在寄存器中的暂存值,重新从内存中读入。

本节的atomic_t类型操作是最基础的,为了介绍下面的内容,必须先介绍它。如果可以使用atomic_t类型代替临界区操作,也可以加快不少速度。本文中的很多语法,是C嵌入汇编的语法,语法说明可以参考 谁不小心的CSDN博客C嵌入汇编。

本文来源:谁不小心的CSDN博客linux内核部件分析(二)——原子性操作atomic_t

原文出自:linux内核部件分析(二)——原子性操作atomic_t

多看书,看好书。

linux内核部件分析(二)——原子性操作atomic

相关文章:

你感兴趣的文章:

标签云: