自旋锁是一个互斥设备,他只能会两个值:“锁定”和“解锁”。它通常实现为某个整数之中的单个位。
任何时候,只要内核代码拥有自旋锁,在相关CPU上的抢占就会被禁止。
适用于自旋锁的核心规则:
(1)任何拥有自旋锁的代码都必须使原子的,除服务中断外(某些情况下也不能放弃CPU,如中断服务也要获得自旋锁。为了避免这种锁陷阱,需要在拥有自旋锁时禁止中断),不能放弃CPU(如休眠,休眠可发生在许多无法预期的地方)。否则CPU将有可能永远自旋下去(死机)。
(2)拥有自旋锁的时间越短越好。
自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,可在不能休眠的代码中使用,例如中断处理例程。
如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁, "自旋"一词就是因此而得名。由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。
自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。
在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。
自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的"不类"。
但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的"类似"。
鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用。如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
自旋锁原语所需包含的文件是 ,以下是自旋锁的内核API:
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;/* 编译时初始化spinlock*/ void spin_lock_init(spinlock_t *lock);/* 运行时初始化spinlock*/
/* 所有spinlock等待本质上是不可中断的,一旦调用spin_lock,在获得锁之前一直处于自旋状态*/ void spin_lock(spinlock_t *lock);/* 获得spinlock*/ void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);/* 获得spinlock,禁止本地cpu中断,保存中断标志于flags*/ void spin_lock_irq(spinlock_t *lock);/* 获得spinlock,禁止本地cpu中断*/ void spin_lock_bh(spinlock_t *lock)/* 获得spinlock,禁止软件中断,保持硬件中断打开*/
/* 以下是对应的锁释放函数*/ void spin_unlock(spinlock_t *lock); void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags); void spin_unlock_irq(spinlock_t *lock); void spin_unlock_bh(spinlock_t *lock);
/* 以下非阻塞自旋锁函数,成功获得,返回非零值;否则返回零*/ int spin_trylock(spinlock_t *lock); int spin_trylock_bh(spinlock_t *lock);
|
在双向驱动程序中使用了自旋锁,是在虚拟网卡发射函数中,在发送数据之前上的锁,发完之后解锁。而在FPGA发送中断中会尝试上锁。因为数据不同于视频,它并不是连续不断的因而不能凑齐2688或接近2688后再送给FPGA,它必需每次有了数据就选择发送。为了不和网卡传来的包有冲突,所以需要用自旋锁。
阅读(1787) | 评论(0) | 转发(0) |