首页 > 编程知识 正文

浅谈自己对教育的理解,原子锁

时间:2023-05-06 05:33:06 阅读:116913 作者:4568

在理解原子自动操作之前,我们知道在修改某个变量时,在组件级至少需要细分为“读-修改-写”三个过程。 也就是说,他们访问存储单元两次,第一次读取原始值,第二次写入新值。

假设在这种情况下,两个cpu同时对同一存储器单元执行“读取-变更-写入”操作。 首先,两个CPU尝试读取同一单元,但是如果存储器仲裁器(串行化访问RAM芯片的操作的硬件电路)伸出手,只允许一个访问而不延迟另一个,则两个读取操作都将被串行化第一次读取完成后,另一个CPU从内存单元读取正好相同(旧)的值。 但是,两个CPU都试图在其存储器单元中写入新的值,总线存储器访问再次被存储器总裁串行化,最终两个写入都成功。 但是,全局结果不正确,因为两个CPU写入相同(新)的值。 因此,这容易导致意想不到的结果。

因此,避免“读-修改-写”命令竞争的最简单方法是确保此类操作在芯片级是原子的。 所有操作都必须用单个指令执行,中断不能中断,其他CPU必须不访问同一存储器单元。 这些小原子操作可以基于其他更灵活的进制方法创建临界区域。

在x86平台上,总的来说,CPU提供三个独立的原子链机制:原子

保证操作、添加LOCK命令前缀和高速缓存一致性协议。

理解原子(atom )的本来意思是"不能再分割的最小粒子",原子进行操作

(atomic operation )是指“不可中断的一个或一系列操作”。 原子操作的简单说明是,当多个线程执行一个操作时,其中一个线程

如果此操作已完全完成或没有执行此操作的步骤,则此操作将

是原子的。 原子操作是其他内核同步方法的基础。

api使atomic_read(v )返回*Vatomic_set(v ) v,I ),使*v成为iatomic_add(i ) I,v,使*v成为iatomic_add(i v )从*v中减去I,返回*v的新值atomic_sub_and_test(i ) I,v,从*v中减去I,如果结果为0,则返回1; 否则,返回0atomic_Inc(v ),将*vatomic_dec(v ) v加1,从*v减去1atomic_inc_return(v ) v,将*v加1 addr ),*addr的第nr比特clear_bit ) nr,addr )清除*addr的第nr比特change _ bit (NR,addr )的type _ sync _ fetch type _ sync _ fetch _ and _ or (type * ptr,type value ); type _ sync _ fetch _ and _ and (type * ptr,type value ); type _ sync _ fetch _ and _ xor (type * ptr,type value ); type _ sync _ fetch _ and _ NAND (type * ptr,type value ); type _ sync _ add _ and _ fetch (type * ptr,type值); type _ sync _ sub _ and _ fetch (type * ptr,type value ); type _ sync _ or _ and _ fetch (type * ptr,type value ); type _ sync _ and _ and _ fetch (type * ptr,type值); type _ sync _ xor _ and _ fetch (type * ptr,type value ); type _ sync _ NAND _ and _ fetch (type * ptr,type value ); 2.dpdk提供的原子操作

staticinlineintrte _ atomic 16 _ cmpset (volatile uint 16 _ t * dst,uint16_t exp,uint16_t src ); staticinlineuint 16 _ trte _ atomic 16 _ exchange (volatile uint 16 _ t * dst,uint16_t val ); staticinlinevoidrte _ atomic 16 _ init (rte _ atomic 16 _ t * v ) staticinlineint 16 _ trte _ atomic 16 _ read ) constRTE

6_t new_value)static inline void rte_atomic16_add(rte_atomic16_t *v, int16_t inc)static inline void rte_atomic16_sub(rte_atomic16_t *v, int16_t dec)static inline void rte_atomic16_inc(rte_atomic16_t *v);static inline void rte_atomic16_dec(rte_atomic16_t *v);static inline int16_t rte_atomic16_add_return(rte_atomic16_t *v, int16_t inc)static inline int16_t rte_atomic16_sub_return(rte_atomic16_t *v, int16_t dec)static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v);static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v);static inline int rte_atomic16_test_and_set(rte_atomic16_t *v);static inline void rte_atomic16_clear(rte_atomic16_t *v)

dpdk提供了16、 32和64位的原子操作API,主要实现原理是使用了**LOCK指令+CMPXCHG指令**。

对于LOCK指令前缀的总线锁,早期CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前
缀“LOCK”(这个前缀表示锁总线),经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指
令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。
随着处理器的发展,对LOCK前缀的实现也在不断进行着性能改善。最近几代处理器中已经支持新的锁技术,若当前访问的内存已经被处理器缓存,LOCK#不会被触发,会用锁缓存的方式代替。这样处理原子操作的开销就在这些特定场景下进一步降低。

CMPXCHG这条指令,它的语义是比较并交换操作数(CAS,Compare And Set)。而用XCHG类的指令做内存操作,处理器会自动地遵循LOCK的语义,可见该指令是一条原子的CAS单指令操作。

源码如下

static inline intrte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src){uint8_t res;asm volatile(MPLOCKED"cmpxchgq %[src], %[dst];""sete %[res];": [res] "=a" (res), /* output */ [dst] "=m" (*dst): [src] "r" (src), /* input */ "a" (exp), "m" (*dst): "memory"); /* no-clobber list */return res;}

本人从dpdk移植了锁实现到自己的github里面 https://github.com/air5005/usg/tree/master/libs/liblock 有兴趣的可以参考.
3.linux kernel
内核的原子锁实现主要在x86结构里面,使用的是lock指令+内存屏障原理
提供的api基本一致,都是分为16、32、64位三种api。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。