原子操作
原子操作意为,不可以中断的一个或一系列操作。多个线程在执行同一个操作时(这个操作可能是几行代码),任何一个线程都是要么完全执行完此操作,要么没有执行。原子操作是内核同步的基石。
处理器上的原子操作
cpu提供了三种独立的原子操作:原子保证操作、加LOCK指令前缀、缓存一致性协议。本质都是防止多个处理器多同一个内存区域进行操作。
CMPXCHG指令(CAS),这个指令接收两个值,旧值、新值。在操作期间,先比较旧值有没有发送改变,没有就把新值替换到内存,旧值不同就不替换,这一些操作是原子性的。存在ABA问题,可以查找资料了解一下。
内核上的原子操作
内核属于软件,软件依赖与上面的硬件原子操作支持。linux中提供了两组原子操作接口:针对整数进行操作、针对单独的位进行操作。
- 原子整数操作:对atomic_t进行处理,原子函数只接受atomic_t类型,所以没有使用int作为整数定义,此类型确保不被编译器进行访问优化,可以屏蔽不同体系之间的差异。
- 原子性和顺序性:原子性上面解释了,顺序性是确保两条及以上的指令他们的执行顺序也不会被修改。
- 原子位操作:是对内存地址进行操作。
原子整数操作:
原子位操作:
读写锁
读写锁实际上是一种自旋锁,它对共享资源的访问操作进行划分,读操作是安全的,写操作是危险的。写操纵是排他的,一个读写锁,可以同时进行多读操作,但是能进行一个写操作,不能同时又读又写。
- 当一个线程持有读锁后,其他线程可以继续获取读锁,但是不能获取写锁。
- 当一个线程获取写锁后,其他线程不可获取锁。
linux读写锁api:
自旋锁
自旋锁和互斥锁类似,在任何时刻,最多只能有一个保持着,但是实现策略不同,对于互斥锁,如果资源被占用,资源申请者就进入睡眠状态。对于自旋锁,如果资源被占用,资源申请者就一直循环获取锁,所以称为自旋。这两个锁各有各的好处坏处,这里不讨论。
linux自旋锁api:
无锁机制
当前,高性能的服务器软件(例如,HTTP加速器)在大部分情况下是运行在多核服务器上的,当前的硬件可以提供32、64或者更多的CPU,在这种高并发的环境下,锁竞争机制有时会比数据拷贝、上下文切换等更伤害系统的性能。因此,在多核环境下,需要把重要的数据结构从锁的保护下移到无锁环境,以提高软件性能。
所以,现在无锁机制变得越来越流行,在特定的场合使用不同的无锁队列,可以节省锁开销,提高程序效率。Linux内核中有无锁队列的实现,可谓简洁而不简单。
Linux内核无锁环形缓冲:
环形缓冲区通常有一个读指针和一个写指针,环形缓冲区的读用户仅仅会影响读指针,而写用户仅仅会影响写指针。如果有多个读写用户访问环形缓冲区,那么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。。采用环形缓冲区的好处是,当一个数据元素被用掉后,其余数据元素不需要移动其存储位置,从而减少拷贝,提高效率。
小结:以上说得机制,DPDK都有相应的实现,学会使用之后再了解实现原理。