不同于mutex中,当获取不到spinlock自旋锁,进程会自选等待(一直运行,spinning),而不是进入睡眠。
spinlock
initial spinlock
spinlock structure
typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
//...
} raw_spinlock_t;
typedef struct spinlock {
union {
struct raw_spinlock rlock;
//...
};
} spinlock_t;
static
通过DEFINE_SPINLOCK()
宏来定义spinlock。include/linux/spinlock_types.h
中定义
#define ___SPIN_LOCK_INITIALIZER(lockname) \
{ \
.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \
SPIN_DEBUG_INIT(lockname) \
SPIN_DEP_MAP_INIT(lockname) }
#define __SPIN_LOCK_INITIALIZER(lockname) \
{ { .rlock = ___SPIN_LOCK_INITIALIZER(lockname) } }
#define __SPIN_LOCK_UNLOCKED(lockname) \
(spinlock_t) __SPIN_LOCK_INITIALIZER(lockname)
#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
dynamic
include/linux/spinlock.h
中定义
#ifdef CONFIG_DEBUG_SPINLOCK
# define spin_lock_init(lock) \
do { \
static struct lock_class_key __key; \
\
__raw_spin_lock_init(spinlock_check(lock), \
#lock, &__key, LD_WAIT_CONFIG); \
} while (0)
#else
# define spin_lock_init(_lock) \
do { \
spinlock_check(_lock); \
*(_lock) = __SPIN_LOCK_UNLOCKED(_lock); \
} while (0)
#endif
比如:
spinlock_t test_spinlock;
spin_lock_init(&test_spinlock);
howto
spinlock使用要分两种上下文:进程上下文(user context)和中断上下文(interrupt context)
1. Locking between User context
操作 | 函数API | |
---|---|---|
lock | *static __always_inline void spin_lock(spinlock_t lock) | 如果lock当前忙,则自旋等待 |
trylock | *static __always_inline int spin_trylock(spinlock_t lock) | 尝试获取lock,成果则锁,失败返回 |
unlock | *static __always_inline void spin_unlock(spinlock_t lock) | 解锁自旋锁 |
checklock | *static __always_inline int spin_is_locked(spinlock_t lock) | 检查是否已上锁 |
示例
//Thread 1
int thread_function1(void *pv)
{
while(!kthread_should_stop()) {
spin_lock(&test_spinlock);
global_variable++;
printk(KERN_INFO "In Thread Function1 %lu\n", global_variable);
spin_unlock(&test_spinlock);
msleep(1000);
}
return 0;
}
//Thread 2
int thread_function2(void *pv)
{
while(!kthread_should_stop()) {
spin_lock(&test_spinlock);
global_variable++;
printk(KERN_INFO "In Thread Function2 %lu\n", global_variable);
spin_unlock(&test_spinlock);
msleep(1000);
}
return 0;
}
2. Locking between Bottom Halves
在不同的下半部之间或者同一个下半部中进行数据共享,自旋锁的使用方法同user context中。
3. Locking between User context and Bottom Halves
在进程上下文与中断下半部之间数据共享,自旋锁使用方法
操作 | 函数API | 说明 |
---|---|---|
lock | spin_lock_bh(spinlock_t *lock) | 会关闭该CPU的软中断 该API会阻止该CPU上softirqs/tasklets/bottom halves的运行 |
unlock | spin_unlock_bh(spinlock_t *lock) | 释放自旋锁,且打开CPU的软中断 |
//Thread
int thread_function(void *pv)
{
while(!kthread_should_stop()) {
spin_lock_bh(&test_spinlock);
global_variable++;
printk(KERN_INFO "In Thread Function %lu\n", global_variable);
spin_unlock_bh(&test_spinlock);
msleep(1000);
}
return 0;
}
/*Tasklet Function*/
void tasklet_fn(unsigned long arg)
{
spin_lock_bh(&test_spinlock);
global_variable++;
printk(KERN_INFO "Executing Tasklet Function : %lu\n", global_variable);
spin_unlock_bh(&test_spinlock);
}
4. Locking between Hard IRQ and Bottom Halves
如果在硬中断和中断下半部之间共享数据,则需要在锁定之前关闭IRQ,因为bottom halves处理过程是可以被hard irq打断的,所以必须关闭硬中断。
操作 | 函数API | 说明 |
---|---|---|
lock | spin_lock_irq(spinlock_t *lock) | 会关闭该CPU的硬中断 |
unlock | spin_unlock_irq(spinlock_t *lock) | 释放自旋锁,打开CPU硬中断 |
/*Tasklet Function*/
void tasklet_fn(unsigned long arg)
{
spin_lock_irq(&test_spinlock);
test_global_variable++;
printk(KERN_INFO "Executing Tasklet Function : %lu\n", test_global_variable);
spin_unlock_irq(&test_spinlock);
}
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
spin_lock_irq(&test_spinlock);
test_global_variable++;
printk(KERN_INFO "Executing ISR Function : %lu\n", test_global_variable);
spin_unlock_irq(&test_spinlock);
/*Scheduling Task to Tasklet*/
tasklet_schedule(tasklet);
return IRQ_HANDLED;
}
但是,万一spin_lock_irq()
之前硬中断就是关闭的怎么办?那么此时spin_unlock_irq()
就会打开了硬中断。所以最好使用下面的安全方法:
操作 | 函数API | 说明 |
---|---|---|
lock | *spin_lock_irqsave(spinlock_t lock, unsigned long flags) | flags参数保存之前硬中断状态(开或关) 再获取自旋锁 |
unlock | *spin_unlock_irqrestore(spinlock_t lock, unsigned long flags) | 释放自旋锁,并根据flags恢复之前硬中断状态 |
5. Locking between Hard IRQs
如果在不同的硬中断之间共享数据,可以使用spin_lock_irqsave()
等API,同第4点。