Linux内核之spinlock自旋锁机制

不同于mutex中,当获取不到spinlock自旋锁,进程会自选等待(一直运行,spinning),而不是进入睡眠

Yes
No
get spinlock
avaliable?
get lock
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说明
lockspin_lock_bh(spinlock_t *lock)会关闭该CPU的软中断
该API会阻止该CPU上softirqs/tasklets/bottom halves的运行
unlockspin_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说明
lockspin_lock_irq(spinlock_t *lock)会关闭该CPU的硬中断
unlockspin_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点。

reference

Linux Device Driver Tutorials – ch 23

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值