自旋锁

XWOS的自旋锁

概述

自旋锁是多核系统中为防止多个处理器同时访问公共的内存区域(称为临界区)而引入的一种锁。 当一个CPU获得自旋锁并访问临界区时,其他CPU只能 自旋 等待锁。 所谓 自旋 ,是指不断循环测试 是否已经解开。

自旋锁只是为SMP的场景设计的一种锁机制。在单核(UP)系统中,并不需要自旋锁。 但为了软件接口的统一,实现了虚假的自旋锁,只是对关闭抢占、中断底半部或中断的封装。

自旋锁还伴随其他操作:内存屏障,关闭调度器的抢占,关闭中断底半部,关闭中断等。

局限性

  • 自旋锁内包含内存内存屏障操作,内存屏障会降低CPU的性能;
  • 自旋锁不区分读和写的情况。

使用自旋锁

自旋锁的初始化

自旋锁是基于原子操作指令实现的,自旋锁结构体很小,核心数据是一个CPU指令能操作的基本数据类型,因此不提供动态创建和删除方法。 用户可以通过 xwos_splk_init() 初始化自旋锁。

多锁

当使用 多个 自旋锁保护临界区时,上锁和解锁顺序必须 保持一致 ,否则会导致死锁。

锁模式

保护 线程 上下文之间的临界区

  • 临界区可嵌套,且只对 线程 上下文是安全的。
        xwos_splk_lock(&lock1);
        /* 临界区1 */
        xwos_splk_lock(&lock2);
        /* 临界区2 */
        xwos_splk_unlock(&lock2);
        /* 临界区1 */
        xwos_splk_unlock(&lock1);
  • 临界区内,只会关闭抢占。可以理解为,在线程层面,临界区内的操作是 原子的
  • 临界区内的数据只能被 线程 的代码访问。
  • 临界区内不能发生 抢占 ,用户不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

保护 任意 上下文之间的单一临界区

  • 临界区对 任意 上下文都是安全的。但临界区必须是单一的,嵌套时会出现以下错误:
        xwos_splk_lock_cpuirq(&lock1);
        /* 临界区1 */
        xwos_splk_lock_cpuirq(&lock2);
        /* 临界区2 */
        xwos_splk_unlock_cpuirq(&lock2);
        /* 临界区1: 错误!中断被打开 */
        xwos_splk_unlock_cpuirq(&lock1);
  • 临界区内,不但会关闭抢占,还会关闭CPU中断。可以理解为在临界区内的操作是 原子的 ,临界区内的数据能被 任意 代码访问。
  • 临界区内不会发生中断,也不能发生 调度 ,用户依然不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

保护 任意 上下文之间的 嵌套 临界区

  • 为了解决关闭中断时临界区嵌套问题,可以使用中断标志保存与恢复的CAPI。
        xwos_splk_lock_cpuirqsv(&lock1, &cpuirq1);
        /* 临界区1 */
        xwos_splk_lock_cpuirqsv(&lock2, &cpuirq2);
        /* 临界区2 */
        xwos_splk_unlock_cpuirqrs(&loc2, cpuirq2); /* 退出临界区2时中断标志还是为关闭 */
        /* 临界区1:不会出现中断被打开的错误 */
        xwos_splk_unlock_cpuirqrs(&loc1, cpuirq1);
  • 临界区内,不但会关闭抢占,还会关闭CPU中断。可以理解为在临界区内的操作是 原子的 ,临界区内的数据能被 任意 代码访问。
  • 临界区内不会发生中断,也不能发生 调度 ,用户依然不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

保护 线程指定中断 上下文之间的临界区

  • 临界区对 线程指定中断 是安全的。但临界区必须是单一的。
        xwos_splk_lock_irqs(&lock, irq_array, num);
        /* 临界区 */
        xwos_splk_unlock_irqs(&lock, irq_array, num);
  • 临界区内,只关闭抢占和指定的中断。可以理解为在线程和指定的中断函数层面,临界区内的操作是 原子的 , 临界区内的数据只能被 线程指定的外设中断 上下文访问。
  • 临界区内不会发生指定的中断,也不会发生 调度 ,但用户依然不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

保护 线程指定中断 上下文之间的 嵌套 临界区

  • 临界区对 线程指定中断 是安全的。临界区可以嵌套。
        xwos_splk_lock_irqssv(&lock1, irq_array, flag1_array, num);
        /* 临界区1 */
        xwos_splk_lock_irqssv(&lock2, irq_array, flag2_array, num);
        /* 临界区2 */
        xwos_splk_unlock_irqsrs(&lock2, irq_array, flag2_array, num);
        /* 临界区1 */
        xwos_splk_unlock_irqsrs(&lock1, irq_array, flag1_array, num);
  • 临界区内,只关闭抢占和指定的中断。可以理解为在线程和指定的中断函数层面,
  • 临界区内的操作是 原子的 ,临界区内的数据只能被 线程指定的外设中断 上下文访问。
  • 临界区内不会发生指定的中断,也不会发生 调度 ,但用户依然不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

保护 线程中断底半部 上下文之间的临界区

  • 临界区对 线程中断底半部 是安全的。临界区可嵌套。
        xwos_splk_lock_bh(&lock1);
        /* 临界区1 */
        xwos_splk_lock_bh(&lock2);
        /* 临界区2 */
        xwos_splk_unlock_bh(&lock2);
        /* 临界区1 */
        xwos_splk_unlock_bh(&lock1);
  • 临界区内,只关闭抢占和中断底半部。可以理解为在线程和中断底半部层面,临界区内的操作是 原子的 , 临界区内的数据只能被 线程中断底半部 上下文访问。
  • 临界区内不会发生 调度 ,但用户依然不能在临界区内使用会导致 睡眠阻塞 的CAPI。
  • CAPI

CAPI参考