zoukankan      html  css  js  c++  java
  • Linux设备驱动程序 之 自旋锁

    概念

    自旋锁可以再不能休眠的代码中使用,比如中断处理例程;在正确使用的情况下,自旋锁通常可以提供比信号量更高的性能;

    一个自旋锁是一个互斥设备,它只能由两个值,锁定和解锁;通常实现为某个整数值中的单个位;希望获得特定锁的代码测试相关位,如果锁可用,则锁定位被设置,而嗲吗继续进入临界区;相反,如果锁被其他人获得,则代码进入忙循环并重复检查这个锁,直到该锁可用为止;这循环就是自旋锁自旋的部分;

    自旋锁在不同的架构上实现有所不同,但是核心概念低于所有系统都都是一样的,当存在某个自旋锁时,等待执行忙循环的处理器做不了任何有用的工作;

    自旋锁最初是为了在多处理器系统上使用而设计的,考虑到并发问题,单处理器工作站在运行可抢占内核时,其行为类似于SMP;因为抢占,即使不打算在SMP系统上运行自己的嗲吗,我们仍然需要实现正确的锁定;

    自旋锁和原子上下文

    适用于自旋锁的规则是:任何拥有自旋锁的代码都必须是原子的,它不能休眠;事实上,它不能因为任何原因放弃处理器,除了服务中断以外(某些情况下此时也不能放弃处理器);

    内核抢占的情况有自旋锁代码本身处理;任何时候,只要内核代码拥有自旋锁,在相关处理器上的抢占就会被禁止;甚至在单处理器上,也必须以同样的方式禁止抢占以避免竞态;

    在拥有锁的时候避免休眠有时候很难做到,休眠可能发生在很多无法预期的地方,当我们编写需要在自旋锁下执行的代码时,必须注意每一个调用的函数;

    另外一种情形,当驱动程序正在执行时,并且获取了一个锁,这个锁控制着对设备的访问;在这个时候,设备产生了一个中断,导致中断处理例程会被调用;而中断处理例程在访问设备之前,也要获取这个锁;在中断处理例程中拥有锁是合法的;但是,中断处理例程在最初拥有锁的代码所在的处理器上运行时,处于自旋获取锁的状态,而此时非中断代码将没有任何机会释放这个锁;处理器将永远自旋下去;为了避免这种情况,我们需要在拥有自旋锁时禁止本地cpu对的中断;

    自旋锁使用的最后一个重要规则:自旋锁必须在可能的最短时间内拥有;拥有自旋锁的时间越长,其他处理器不得不自旋等待释放该自旋锁的时间就越长;长时间拥有锁将阻止当前处理器的调度,这意味着更高优先级的进程不得不等待;为了避免这种问题,谨记拥有锁的时间越短越好;

    自旋锁使用

    使用自旋锁需要包含<linux/spinlock.h>头文件;实际锁具有spinlock_t类型,和其他任何数据结构类似,一个自旋锁必须被初始化;

    编译期间静态的声明和初始化使用如下方式:

    1 #define DEFINE_SPINLOCK(x)

    或者可以调用下面宏对spinlock进行运行时的动态初始化:

    1 #define spin_lock_init(_lock)

    在进入临界区之前,需要使用spin_lock相关函数获取锁:注意:所有的自旋锁等待在本质上都是不可中断的,一旦调用了spin_lock,在获得锁之前将一直处于自旋状态;

     1 static __always_inline void spin_lock(spinlock_t *lock)
     2 
     3 /* 禁止软中断,硬件中断是打开的 */
     4 static __always_inline void spin_lock_bh(spinlock_t *lock)
     5 
     6 /* 禁止本地中断,不保存中断状态,需要确保没有其他代码禁止本地处理器中断 */
     7 static __always_inline void spin_lock_irq(spinlock_t *lock)
     8 
     9 /* 禁止本地处理器中断,保存中断状态 */
    10 #define spin_lock_irqsave(lock, flags)                

    在离开临界区时,要释放已经获取的锁,可以使用下面方法:

    1 static __always_inline void spin_unlock(spinlock_t *lock)
    2 
    3 static __always_inline void spin_unlock_bh(spinlock_t *lock)
    4 
    5 static __always_inline void spin_unlock_irq(spinlock_t *lock)
    6 
    7 static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)

    内核还提供了非阻塞的锁定方法:

    1 static __always_inline int spin_trylock(spinlock_t *lock)
    2 
    3 static __always_inline int spin_trylock_bh(spinlock_t *lock)
    4 
    5 static __always_inline int spin_trylock_irq(spinlock_t *lock)
    6 
    7 #define spin_trylock_irqsave(lock, flags)
    读写自旋锁

    内核提供了自旋锁的读/写形式,这种锁允许任意数量的读取者同时进入临界区,但写入者必须互斥访问,即写者会自旋等待获取锁;读/写自旋锁使用rwlock_t类型,在<linux/rwlock_types.h>和<linux/rwlock.h>中定义,使用读写自旋锁需要包含这两个头文件;

    静态声明和初始化:

    1 #define DEFINE_RWLOCK(x)

    动态声明和初始化:

    1 # define rwlock_init(lock)

    获取锁的方法如下:

  • 相关阅读:
    LINUX重启MYSQL的命令
    如何在linux下实现mysql数据库每天自动备份
    mysql 2013错误解决
    mysql按年度、季度、月度、周、日统计查询的sql语句
    MySQL 时间戳(Timestamp)函数
    jQuery 选择器大全总结
    使用Git的Push出现rejected
    js实现分页的几个源码,看完基本就懂了
    Get,Post请求中文乱码问题有效解决方法
    web应用中文乱码问题的原因分析
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11760722.html
Copyright © 2011-2022 走看看