手机网站制作机构,猎头公司主要做什么,wordpress用户管理,做ppt高手 一定要常去这八个网站背景Kernel版本#xff1a;4.14ARM64处理器#xff0c;Contex-A53#xff0c;双核使用工具#xff1a;Source Insight 3.5#xff0c; Visio1. 概述吹起并发机制研究的进攻号角了#xff01;作为第一篇文章#xff0c;应该提纲挈领的介绍下并发。什么是并发#xff0c;… 背景Kernel版本4.14ARM64处理器Contex-A53双核使用工具Source Insight 3.5 Visio1. 概述吹起并发机制研究的进攻号角了作为第一篇文章应该提纲挈领的介绍下并发。什么是并发并发就是你有两个儿子同时抢一个玩具玩你一巴掌打在你大儿子手上小儿子拿到了玩具。并发是指多个执行流访问同一个资源并发引起竞态。来张图吧图中每一种颜色代表一种竞态情况主要归结为三类进程与进程之间单核上的抢占多核上的SMP进程与中断之间中断又包含了上半部与下半部中断总是能打断进程的执行流中断与中断之间外设的中断可以路由到不同的CPU上它们之间也可能带来竞态目前内核中提供了很多机制来处理并发问题spinlock就是其中一种。spinlock就是大家熟知的自旋锁它的特点是自旋锁保护的区域不允许睡眠可以用在中断上下文中。自旋锁获取不到时CPU会忙等待并循环测试等待条件。自旋锁一般用于保护很短的临界区。下文将进一步揭开神秘的面纱。2. spinlock原理分析2.1 spin_lock/spin_unlock先看一下函数调用流程spin_lock操作中关闭了抢占也就是其他进程无法再来抢占当前进程了spin_lock函数中关键逻辑需要依赖于体系结构的实现也就是arch_spin_lock函数spin_unlock函数中关键逻辑需要依赖于体系结构的实现也就是arch_spin_unlock函数直接看ARM64中这个arch_spin_lock/arch_spin_unlock函数的实现吧static inline void arch_spin_lock(arch_spinlock_t *lock)
{unsigned int tmp;arch_spinlock_t lockval, newval;asm volatile(/* Atomically increment the next ticket. */ARM64_LSE_ATOMIC_INSN(/* LL/SC */prfm pstl1strm, %3\n
1: ldaxr %w0, %3\nadd %w1, %w0, %w5\nstxr %w2, %w1, %3\ncbnz %w2, 1b\n,/* LSE atomics */mov %w2, %w5\nldadda %w2, %w0, %3\n__nops(3))/* Did we get the lock? */eor %w1, %w0, %w0, ror #16\ncbz %w1, 3f\n/** No: spin on the owner. Send a local event to avoid missing an* unlock before the exclusive load.*/sevl\n
2: wfe\nldaxrh %w2, %4\neor %w1, %w2, %w0, lsr #16\ncbnz %w1, 2b\n/* We got the lock. Critical p starts here. */
3:: r (lockval), r (newval), r (tmp), Q (*lock): Q (lock-owner), I (1 TICKET_SHIFT): memory);
}static inline void arch_spin_unlock(arch_spinlock_t *lock)
{unsigned long tmp;asm volatile(ARM64_LSE_ATOMIC_INSN(/* LL/SC */ ldrh %w1, %0\n add %w1, %w1, #1\n stlrh %w1, %0,/* LSE atomics */ mov %w1, #1\n staddlh %w1, %0\n__nops(1)): Q (lock-owner), r (tmp):: memory);
}
spinlock的核心思想是基于tickets的机制每个锁的数据结构arch_spinlock_t中维护两个字段next和owner只有当next和owner相等时才能获取锁每个进程在获取锁的时候next值会增加当进程在释放锁的时候owner值会增加如果有多个进程在争抢锁的时候看起来就像是一个排队系统FIFO ticket spinlock上边的代码中核心逻辑在于asm volatile()内联汇编中有点迷糊吗把核心逻辑翻译成C语言类似于下边asm volatile内联汇编中有很多独占的操作指令只有基于指令的独占操作才能保证软件上的互斥简单介绍如下ldaxrLoad-Acquire Exclusive Register derives an address from a base register value, loads a 32-bit word or 64-bit doubleword from memory, and writes it to a register从内存地址中读取值到寄存器中独占访问stxrStore Exclusive Register stores a 32-bit or a 64-bit doubleword from a register to memory if the PE has exclusive access to the memory address将寄存器中的值写入到内存中并需要返回是否独占访问成功eorBitwise Exclusive OR执行独占的按位或操作ldaddaAtomic add on word or doubleword in memory atomically loads a 32-bit word or 64-bit doubleword from memory, adds the value held in a register to it, and stores the result back to memory原子的将内存中的数据进行加值处理并将结果写回到内存中此外还需要提醒一点的是在arch_spin_lock中当自旋等待时会执行WFE指令这条指令会让CPU处于低功耗的状态其他CPU可以通过SEV指令来唤醒当前CPU。如果说了这么多你还是没有明白那就再来一张图吧2.2 spin_lock_irq/spin_lock_bh自旋锁还有另外两种形式那就是在持有锁的时候不仅仅关掉抢占还会把本地的中断关掉或者把下半部关掉本质上是把软中断关掉。这种锁用来保护临界资源既会被进程访问也会被中断访问的情况。看一下调用流程图可以看到这两个函数中实际锁的机制实现跟spin_lock是一样的额外提一句spin_lock_irq还有一种变种形式spin_lock_irqsave该函数会将当前处理器的硬件中断状态保存下来__local_bh_disable_ip是怎么实现的呢貌似也没有看到关抢占有必要前情回顾一下了如果看过之前的文章的朋友应该见过下边这张图片thread_info-preempt_count值就维护了各种状态针对该值的加减操作就可以进行状态的控制3. rwlock读写锁读写锁是自旋锁的一种变种分为读锁和写锁有以下特点可以多个读者同时进入临界区读者与写者互斥写者与写者互斥先看流程分析图看一下arch_read_lock/arch_read_unlock/arch_write_lock/arch_write_unlock源代码static inline void arch_read_lock(arch_rwlock_t *rw)
{unsigned int tmp, tmp2;asm volatile( sevl\nARM64_LSE_ATOMIC_INSN(/* LL/SC */1: wfe\n2: ldaxr %w0, %2\n add %w0, %w0, #1\n tbnz %w0, #31, 1b\n stxr %w1, %w0, %2\n cbnz %w1, 2b\n__nops(1),/* LSE atomics */1: wfe\n2: ldxr %w0, %2\n adds %w1, %w0, #1\n tbnz %w1, #31, 1b\n casa %w0, %w1, %2\n sbc %w0, %w1, %w0\n cbnz %w0, 2b): r (tmp), r (tmp2), Q (rw-lock):: cc, memory);
}static inline void arch_read_unlock(arch_rwlock_t *rw)
{unsigned int tmp, tmp2;asm volatile(ARM64_LSE_ATOMIC_INSN(/* LL/SC */1: ldxr %w0, %2\n sub %w0, %w0, #1\n stlxr %w1, %w0, %2\n cbnz %w1, 1b,/* LSE atomics */ movn %w0, #0\n staddl %w0, %2\n__nops(2)): r (tmp), r (tmp2), Q (rw-lock):: memory);
}static inline void arch_write_lock(arch_rwlock_t *rw)
{unsigned int tmp;asm volatile(ARM64_LSE_ATOMIC_INSN(/* LL/SC */ sevl\n1: wfe\n2: ldaxr %w0, %1\n cbnz %w0, 1b\n stxr %w0, %w2, %1\n cbnz %w0, 2b\n__nops(1),/* LSE atomics */1: mov %w0, wzr\n2: casa %w0, %w2, %1\n cbz %w0, 3f\n ldxr %w0, %1\n cbz %w0, 2b\n wfe\n b 1b\n3:): r (tmp), Q (rw-lock): r (0x80000000): memory);
}static inline void arch_write_unlock(arch_rwlock_t *rw)
{asm volatile(ARM64_LSE_ATOMIC_INSN( stlr wzr, %0, swpl wzr, wzr, %0): Q (rw-lock) :: memory);
}
知道你们不爱看汇编代码那么翻译成C语言的伪代码看看吧读写锁数据结构arch_rwlock_t中只维护了一个字段volatile unsigned int lock其中bit[31]用于写锁的标记bit[30:0]用于读锁的统计读者在获取读锁的时候高位bit[31]如果为1表明正有写者在访问临界区这时候会进入自旋的状态如果没有写者访问那么直接去自加rw-lock的值从逻辑中可以看出是支持多个读者同时访问的读者在释放锁的时候直接将rw-lock自减1即可写者在获取锁的时候判断rw-lock的值是否为0这个条件显得更为苛刻也就是只要有其他读者或者写者访问那么都将进入自旋没错它确实很霸道只能自己一个人持有写者在释放锁的时候很简单直接将rw-lock值清零即可缺点由于读者的判断条件很苛刻假设出现了接二连三的读者来访问临界区那么rw-lock的值将一直不为0也就是会把写者活活的气死噢是活活的饿死。读写锁当然也有类似于自旋锁的关中断、关底半部的形式read_lock_irq/read_lock_bh/write_lock_irq/write_lock_bh原理都类似不再赘述了。4. seqlock顺序锁顺序锁也区分读锁与写锁它的优点是读者不会把写者给饿死。来看一下流程图顺序锁的读锁有三种形式无加锁访问读者在读临界区之前先读取序列号退出临界区操作后再读取序列号进行比较如果发现不相等说明被写者更新内容了需要重新再读取临界区所以这种情况下可能给读者带来的开销会大一些加锁访问实际是spin_lock/spin_unlock仅仅是接口包装了一下而已因此对读和写都是互斥的在形式1和形式2中动态选择如果有写者在写临界区读者化身为自旋锁没有写者在写临界区则化身为顺序无锁访问顺序锁的写锁只有一种形式本质上是用自旋锁来保护临界区然后再把序号值自加处理顺序锁也有一些局限的地方比如采用读者的形式1的话临界区中存在地址指针操作如果写者把地址进行了修改那就可能造成访问错误了说明一下流程图中的smp_rmb/smp_wmb这两个函数是内存屏障操作作用是告诉编译器内存中的值已经改变之前对内存的缓存缓存到寄存器都需要抛弃屏障之后的内存操作需要重新从内存load而不能使用之前寄存器缓存的值内存屏障就像是代码中一道不可逾越的屏障屏障之前的load/store指令不能跑到屏障的后边同理后边的也不能跑到前边顺序锁也同样存在关中断和关下半部的形式原理基本都是一致的不再啰嗦了。最近在项目中遇到了RCU Stall的问题下一个topic就先来看看RCU吧其他的并发机制都会在路上Just keep growing and fuck everthing else收工如果觉得文档对您有帮助那就点个在看吧谢谢。推荐阅读专辑|Linux文章汇总专辑|程序人生嵌入式Linux微信扫描二维码关注我的公众号