嵌入式技术
在平时的工作中,作为开发人员经常碰到这样的问题:多线程或多进程共享的数据如何进行保护,如果发生进程上下文切换或中断上下文切换都可能使共享数据发生争抢问题。这时候就可以考虑用锁了。如果是进程上下文切换引起的可以考虑用信号量或mutex互斥锁,但如果发生在中断上下文,这时候信号量和mutex就无法使用了,因为这两种锁机制是可以睡眠的,而中断上下文又禁止睡眠,这时,spin_lock就是我们最好的选择了。
现代CPU一般都是多核多CPU的SMP架构,在这种情况下就有可能出现多个CPU要共同访问同一个资源的问题,此时就需要对资源进行保护,确保同一个时刻只有一个CPU正在访问修改资源变量。锁就是在这种背景下诞生的,锁的种类有很多,应用场景也不同,本篇我们主要介绍spinlock自旋锁。
如果一个全局的资源被多个进程上下文访问,此时,内核是如何执行的呢?对于没有开启内核可抢占
选项的内核,所有的系统调用都是串行执行的,并不存在资源竞争的问题。但是。对于现在的大部分内核来说可抢占
选项是开启的。
假如现在有一个共享资源S,有两个进程A和B,都需要访问共享资源S
执行流程大概是这样的:
进程A访问资源S的时候发生了中断,中断去唤醒优先级更高的进程B,中断返回的时候,发生进程切换,优先级更高的进程B执行,进程B访问共享资源S,如果没有加锁保护,此时进程A和进程B都访问了共享资源S,导致程序的执行结果不正确。如果通过spin_lock加锁保护,进程A访问共享资源S前获取spin_lock,此时发生了中断,优先级更高的进程B开始执行,进程B会去获取spin_lock,由于spin_lock被进程A所持有,导致进程B获取spin_lock失败,进程B会死等直到进程A释放了spin_lock,然后进程B就可以获取spin_lock,访问共享资源S。
spinlock
结构体定义在头文件include/linux/spinlock_types.h
typedef struct spinlock {
union {
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
spinlock
结构体变量的定义有两种,一种是静态定义,一种是动态定义。
#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) //静态定义
spinlock_t lock;
spin_lock_init (&lock); //动态定义
spinlock
接口函数介绍,这些函数是驱动编程内核编程的时候会用到的。
位于include/linux/spinlock.h头文件中
static __always_inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
{
return &lock- >rlock;
}
#define spin_lock_init(_lock) \\
do { \\
spinlock_check(_lock); \\
raw_spin_lock_init(&(_lock)- >rlock); \\
} while (0)
static __always_inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock- >rlock);
}
static __always_inline void spin_lock_bh(spinlock_t *lock)
{
raw_spin_lock_bh(&lock- >rlock);
}
static __always_inline int spin_trylock(spinlock_t *lock)
{
return raw_spin_trylock(&lock- >rlock);
}
#define spin_lock_nested(lock, subclass) \\
do { \\
raw_spin_lock_nested(spinlock_check(lock), subclass); \\
} while (0)
#define spin_lock_nest_lock(lock, nest_lock) \\
do { \\
raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock); \\
} while (0)
static __always_inline void spin_lock_irq(spinlock_t *lock)
{
raw_spin_lock_irq(&lock- >rlock);
}
#define spin_lock_irqsave(lock, flags) \\
do { \\
raw_spin_lock_irqsave(spinlock_check(lock), flags); \\
} while (0)
#define spin_lock_irqsave_nested(lock, flags, subclass) \\
do { \\
raw_spin_lock_irqsave_nested(spinlock_check(lock), flags, subclass); \\
} while (0)
static __always_inline void spin_unlock(spinlock_t *lock)
{
raw_spin_unlock(&lock- >rlock);
}
static __always_inline void spin_unlock_bh(spinlock_t *lock)
{
raw_spin_unlock_bh(&lock- >rlock);
}
static __always_inline void spin_unlock_irq(spinlock_t *lock)
{
raw_spin_unlock_irq(&lock- >rlock);
}
static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
raw_spin_unlock_irqrestore(&lock- >rlock, flags);
}
static __always_inline int spin_trylock_bh(spinlock_t *lock)
{
return raw_spin_trylock_bh(&lock- >rlock);
}
static __always_inline int spin_trylock_irq(spinlock_t *lock)
{
return raw_spin_trylock_irq(&lock- >rlock);
}
#define spin_trylock_irqsave(lock, flags) \\
({ \\
raw_spin_trylock_irqsave(spinlock_check(lock), flags); \\
})
static __always_inline int spin_is_locked(spinlock_t *lock)
{
return raw_spin_is_locked(&lock- >rlock);
}
static __always_inline int spin_is_contended(spinlock_t *lock)
{