linux内核互斥简介

内核互斥技术,从是否睡眠角度分为两类:
一、可能睡眠:
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);
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值