内核互斥技术,从是否睡眠角度分为两类:
一、可能睡眠:
1、信号量(读写信号量);
2、互斥锁(实时互斥锁);
申请这些锁的时候,如果锁被其他进程占有,进程将会睡眠,进程切换代价很高,适合临界区执行时间比较长的情况;
二、不会睡眠:
1、原子变量;
2、自旋锁(读写自旋锁:允许多个读者同时进入临界区、顺序锁:读者不会阻塞写者);
申请这些锁的时候,如果锁被其他进程占有,进程自旋(忙)等待;
三、其他互斥
1、禁止内核抢占,防止被当前处理器上的其他进程抢占;
2、禁止软中断,防止被当前处理器上的软中断抢占;
3、禁止硬中断,防止被当前处理器上的硬中断抢占;
▶信号量:允许多个进程同时进入临界区,比如停车场;
1、结构体定义:
/* Please don't access any members of this structure directly */
struct semaphore {
raw_spinlock_t lock;//自旋锁,用来保护信号量的其他成员
unsigned int count;//计数值,表示还可以允许多少个进程进入临界区
struct list_head wait_list;//等待进入临界区的进程链表
};
2、静态初始化:
__SEMAPHORE_INITIALIZER(name, n);
#define DEFINE_SEMAPHORE(name) struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
DEFINE_SEMAPHORE(name);
3、动态初始化:
static inline void sema_init(struct semaphore *sem, int val);
4、获取:
//获取信号量,如果计数值是0,进入深度睡眠
extern void down(struct semaphore *sem);
//获取信号量,如果计数值是0,进入轻度睡眠
extern int __must_check down_interruptible(struct semaphore *sem);
//获取信号量,如果计数值是0,进入中度睡眠
extern int __must_check down_killable(struct semaphore *sem);
//获取信号量,如果计数值是0,进程不等待
extern int __must_check down_trylock(struct semaphore *sem);
//获取信号量,指定等待时间
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
5、释放
//释放信号量
extern void up(struct semaphore *sem);
▶读写信号量:允许多个读者进入临界区,读写互斥,写写互斥;
1、结构体定义:
/*
* For an uncontended rwsem, count and owner are the only fields a task
* needs to touch when acquiring the rwsem. So they are put next to each
* other to increase the chance that they will share the same cacheline.
*
* In a contended rwsem, the owner is likely the most frequently accessed
* field in the structure as the optimistic waiter that holds the osq lock
* will spin on owner. For an embedded rwsem, other hot fields in the
* containing structure should be moved further away from the rwsem to
* reduce the chance that they will share the same cacheline causing
* cacheline bouncing problem.
*/
struct rw_semaphore {
atomic_long_t count;
/*
* Write owner or one of the read owners as well flags regarding
* the current state of the rwsem. Can be used as a speculative
* check to see if the write owner is running on the cpu.
*/
atomic_long_t owner;
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* spinner MCS lock */
#endif
raw_spinlock_t wait_lock;
struct list_head wait_list;
#ifdef CONFIG_DEBUG_RWSEMS
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
2、静态初始化:
#define DECLARE_RWSEM(name) struct rw_semaphore name = __RWSEM_INITIALIZER(name)
DECLARE_RWSEM(name);
3、动态初始化:
init_rwsem(sem);
4、获取
//申请读锁,如果写者占有写锁或者正在等待写锁,进程深度睡眠
extern void down_read(struct rw_semaphore *sem);
//申请读锁,不会等待
/*
* trylock for reading -- returns 1 if successful, 0 if contention
*/
extern int down_read_trylock(struct rw_semaphore *sem);
//申请写锁,如果写者占有写锁或者读者占有读锁,进程深度睡眠
extern void down_write(struct rw_semaphore *sem);
//申请写锁,如果写者占有写锁或者读者占有读锁,进程中度睡眠
extern int __must_check down_write_killable(struct rw_semaphore *sem);
//申请写锁,不会等待
/*
* trylock for writing -- returns 1 if successful, 0 if contention
*/
extern int down_write_trylock(struct rw_semaphore *sem);
5、释放
extern void up_read(struct rw_semaphore *sem);
extern void up_write(struct rw_semaphore *sem);
//把写锁降级为读锁
/*
* downgrade write lock to read lock
*/
extern void downgrade_write(struct rw_semaphore *sem);
▶互斥锁:只允许一个进程进入临界区;
1、结构体定义:
/*
* Simple, straightforward mutexes with strict semantics:
*
* - only one task can hold the mutex at a time
* - only the owner can unlock the mutex
* - multiple unlocks are not permitted
* - recursive locking is not permitted
* - a mutex object must be initialized via the API
* - a mutex object must not be initialized via memset or copying
* - task may not exit with mutex held
* - memory areas where held locks reside must not be freed
* - held mutexes must not be reinitialized
* - mutexes may not be used in hardware or software interrupt
* contexts such as tasklets and timers
*
* These semantics are fully enforced when DEBUG_MUTEXES is
* enabled. Furthermore, besides enforcing the above rules, the mutex
* debugging code also implements a number of additional features
* that make lock debugging easier and faster:
*
* - uses symbolic names of mutexes, whenever they are printed in debug output
* - point-of-acquire tracking, symbolic lookup of function names
* - list of all locks held in the system, printout of them
* - owner tracking
* - detects self-recursing locks and prints out all relevant info
* - detects multi-task circular deadlocks and prints out all affected
* locks and tasks (and only those tasks)
*/
struct mutex {
atomic_long_t owner;
spinlock_t wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
struct list_head wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
2、静态初始化:
#define DEFINE_MUTEX(mutexname) struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
DEFINE_MUTEX(mutexname);
3、动态初始化:
mutex_init(mutex)
4、获取:
//申请互斥锁,如果锁被占有,进程深度睡眠
extern void mutex_lock(struct mutex *lock);
//申请互斥锁,如果锁被占有,进程轻度睡眠
extern int __must_check mutex_lock_interruptible(struct mutex *lock);
//申请互斥锁,如果锁被占有,进程中度睡眠
extern int __must_check mutex_lock_killable(struct mutex *lock);
//
extern void mutex_lock_io(struct mutex *lock);
//申请互斥锁,如果锁被占有,不等待
/*
* NOTE: mutex_trylock() follows the spin_trylock() convention,
* not the down_trylock() convention!
*
* Returns 1 if the mutex has been acquired successfully, and 0 on contention.
*/
extern int mutex_trylock(struct mutex *lock);
5、释放:
extern void mutex_unlock(struct mutex *lock);
▶实时互斥锁:实现优先级继承,解决优先级反转的问题;
优先级反转:假设进程1的优先级低,进程2的优先级高。进程1占有互斥锁,进程2申请互斥锁,所以进程2必须睡眠等待。如果进程3的优先级在进程1和进程2之间,进程3可以抢占进程1,导致进程1占有互斥锁的时间延长,进程2等待的时间延长,结果上出现进程3抢占进程2。
1、结构体定义:
/**
* The rt_mutex structure
*
* @wait_lock: spinlock to protect the structure
* @waiters: rbtree root to enqueue waiters in priority order;
* caches top-waiter (leftmost node).
* @owner: the mutex owner
*/
struct rt_mutex {
raw_spinlock_t wait_lock;
struct rb_root_cached waiters;
struct task_struct *owner;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
2、静态初始化:
#define DEFINE_RT_MUTEX(mutexname) struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname)
DEFINE_RT_MUTEX(mutexname);
3、动态初始化:
rt_mutex_init(mutex);
4、获取:
//申请实时互斥锁,如果锁被占有,进程深度睡眠
extern void rt_mutex_lock(struct rt_mutex *lock);
//申请实时互斥锁,如果锁被占有,进程轻度睡眠
extern int rt_mutex_lock_interruptible(struct rt_mutex *lock);
//申请实时互斥锁,如果锁被占有,进程不等待
extern int rt_mutex_trylock(struct rt_mutex *lock);
5、释放:
extern void rt_mutex_unlock(struct rt_mutex *lock);
▶原子变量:用来实现对整数的互斥访问,通常用来实现计数器;
1、结构体定义:
typedef struct {
int counter;
} atomic_t;
2、静态初始化:
#define ATOMIC_INIT(i) { (i) }
atomic_t <name> = ATOMIC_INIT(n);
3、动态初始化:
#define atomic_set(v,i) WRITE_ONCE(((v)->counter), (i))
atomic_set(v,i);
4、操作函数:
//读取原子变量v的值
atomic_read(v);
//原子变量v的值加i
atomic_add(i, v);
//原子变量v的值加1
atomic_inc(v);
//原子变量v的值减i
atomic_sub(i, v);
//原子变量v的值减1
atomic_dec(v);
▶自旋锁:用于处理器之间的互斥,进程、软中断、硬中断都可以使用;
目前内核的自旋锁是排队自旋锁(queued spinlock、FIFO ticket spinlock):
1、锁拥有排队号和服务号,服务号是当前占有锁的进程的排队号;
2、每个进程申请锁的时候,首先申请一个排队号,然后轮询锁的服务号是否等于自己的排队号;
3、当进程释放锁时,把服务号加1,下一个进程继续轮询。
1、结构体定义:
typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} raw_spinlock_t;
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;
2、静态初始化:
#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
DEFINE_SPINLOCK(x);
3、动态初始化:
spin_lock_init(lock);
4、获取:
//申请自旋锁,如果锁被其他处理器占有,当前处理器自旋等待
static __always_inline void spin_lock(spinlock_t *lock);
//申请自旋锁,如果锁被其他处理器占有,当前处理器不等待
static __always_inline int spin_trylock(spinlock_t *lock);
//申请自旋锁,并且禁止当前处理器的软中断
static __always_inline void spin_lock_bh(spinlock_t *lock);
//申请自旋锁,并且禁止当前处理器的硬中断
static __always_inline void spin_lock_irq(spinlock_t *lock);
5、释放:
//释放自旋锁
static __always_inline void spin_unlock(spinlock_t *lock);
//释放自旋锁,并且开启当前处理器的软中断
static __always_inline void spin_unlock_bh(spinlock_t *lock)
//释放自旋锁,并且开启当前处理器的硬中断
static __always_inline void spin_unlock_irq(spinlock_t *lock)
▶读写自旋锁:允许多个读者同时进入临界区,读者和写者互斥,写者和写者互斥;
1、结构体定义:
typedef struct {
u32 lock;
} arch_rwlock_t;
typedef struct {
arch_rwlock_t raw_lock;
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} rwlock_t;
缺陷:如果读者很多,写者很难获取写锁,可能饿死。
改进:排队读写锁,如果写者正在等待写锁,读者申请读锁时自旋等待,写者在锁被释放后优先得到写锁。
2、静态初始化:
#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x)
DEFINE_RWLOCK(x)
3、动态初始化:
rwlock_init(lock);
4、获取:
//申请读锁,如果写者占有写锁,当前处理器自选等待
read_lock(lock);
//申请读锁,不等待
read_trylock(lock);
//申请读锁,并且禁止当前处理器的软中断
read_lock_bh(lock);
//申请读锁,并且禁止当前处理器的硬中断
read_lock_irq(lock);
//申请读锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断
read_lock_irqsave(lock, flags);
//申请写锁,如果写者占有写锁或者读者占有读锁,当前处理器自选等待
write_lock(lock);
//申请写锁,不等待
write_trylock(lock);
//申请写锁,并且禁止当前处理器的软中断
write_lock_bh(lock);
//申请写锁,并且禁止当前处理器的硬中断
write_lock_irq(lock);
//申请写锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断
write_lock_irqsave(lock, flags);
5、释放:
//释放读锁
read_unlock(lock);
//释放读锁,并且开启当前处理器的软中断
read_unlock_bh(lock);
//释放读锁,并且开启当前处理器的硬中断
read_unlock_irq(lock);
//释放读锁,并且恢复当前处理器的硬中断状态
read_unlock_irqrestore(lock, flags);
//释放写锁
write_unlock(lock);
//释放写锁,并且开启当前处理器的软中断
write_unlock_bh(lock);
//释放写锁,并且开启当前处理器的硬中断
write_unlock_irq(lock);
//释放写锁,并且恢复当前处理器的硬中断状态
write_unlock_irqrestore(lock, flags);
▶顺序锁:读者不会阻塞写者,读者读数据的时候写者可以写数据。顺序锁有序列号,写者把序列号加1,读者检测到序列号产生变化,发现写者修改了数据,将会重试,读者的代价比较高,但不会出现写者饿死的情况。
1、结构体定义:
/*
* Sequential locks (seqlock_t)
*
* Sequence counters with an embedded spinlock for writer serialization
* and non-preemptibility.
*
* For more info, see:
* - Comments on top of seqcount_t
* - Documentation/locking/seqlock.rst
*/
typedef struct {
/*
* Make sure that readers don't starve writers on PREEMPT_RT: use
* seqcount_spinlock_t instead of seqcount_t. Check __SEQ_LOCK().
*/
seqcount_spinlock_t seqcount;
spinlock_t lock;
} seqlock_t;
2、静态初始化:
#define DEFINE_SEQLOCK(sl) seqlock_t sl = __SEQLOCK_UNLOCKED(sl)
DEFINE_SEQLOCK(sl);
3、动态初始化:
seqlock_init(sl);
4、使用:
▶禁止内核抢占:
//把抢占计数减1,如果抢占计数器为0,重新调度进程
preempt_enable();
//把抢占计数减1,如果抢占计数器为0,不调度进程
preempt_enable_no_resched():
//禁止抢占
preempt_disable():
▶禁止软中断:以下接口只对本处理器有效;
//开启软中断,把当前进程的软中断计数减2,如果软中断、硬中断、不可屏蔽中断的计数都为0,并且有软中断需要处理,那么处理软中断
local_bh_enable();
//禁止软中断,把当前进程的软中断计数加2,只能禁止本处理器的软中断
local_bh_disable();
▶禁止硬中断:以下接口只对本处理器有效;
//不可嵌套
local_irq_enable();
//可嵌套
local_irq_restore(flags);
//不可嵌套
local_irq_disable();
//可嵌套
local_irq_save(flags);