Home 内核-mutex(互斥锁)
Post
Cancel

内核-mutex(互斥锁)

实现原理

互斥锁类似于count值为1时的信号量。内核的实现也基本如此,但因为count固定为1,Linux为此重新定义了新的数据结构mutex, 专门指代互斥锁,并对原本的down和up操作作了优化和扩展。可以认为互斥锁和count为1的信号量没有本质区别,只是内核的一个特化用例。

和信号量的区别

互斥锁是二值的,只有锁定和非锁定两个状态,互斥锁适用于实现对临界区(Critical Section)的互斥访问,即同一时间只允许一个进程进入临界区进行访问。在进入临界区之前,需要获取互斥锁;在离开临界区之后,需要释放互斥锁。

信号量可以是多值的,既可以用于进程间的同步,也可以用于进程间的互斥。它可以管理一定数量的资源。

互斥锁更专注于互斥访问共享资源的场景,是一个特例,但是优化更好。信号量更灵活,既可以用于资源管理,也可以用于进程间的同步。选择使用哪种机制取决于具体的应用场景和需求。

相关API接口

详细内容位于内核头文件 /include/linux/mutex.h 中。

互斥锁结构体:struct mutex,该结构体目前已有两种定义,与内核的选项有关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef CONFIG_PREEMPT_RT
struct mutex {
	atomic_long_t		owner;
	raw_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
};

#else

struct mutex {
	struct rt_mutex_base	rtmutex;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};
#endif

初始化,也有静态和动态的

1
2
3
4
5
6
7
8
9
10
11
//静态初始化
#define DEFINE_MUTEX(mutexname) \
	struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)

//动态初始化
#define mutex_init(mutex)						\
do {									\
	static struct lock_class_key __key;				\
									\
	__mutex_init((mutex), #mutex, &__key);				\
} while (0)

加解锁操作

注意,互斥锁是可以嵌套的,也就是说一个线程在持有互斥锁的同时可以再次获取同一个互斥锁。 这种情况下,互斥锁会记录嵌套的次数,并在最后一次释放锁时才真正释放。

1
2
3
4
5
6
void             mutex_lock(struct mutex *lock);
int __must_check mutex_lock_interruptible(struct mutex *lock);

void mutex_unlock(struct mutex *lock);
int mutex_trylock(struct mutex *lock);
bool mutex_is_locked(struct mutex *lock);

基本API如上,用来加解锁,以实现共享资源互斥访问。

实时互斥锁(rtmutex)

rtmutex(Real-Time Mutex)是一种实时互斥锁,它是对普通互斥锁(mutex)的改进和扩展。 rtmutex旨在提供更高效和更可预测的互斥锁机制,特别适用于实时系统中对锁的要求更为严格的场景。 与普通互斥锁相比,rtmutex引入了更多的数据结构和算法,以提供更高的性能和实时性。

在关系方面,rtmutex可以说是互斥锁的一种变种或改进版本。rtmutex相对于普通互斥锁具有一些显著的区别和优势:

  1. 实时性能:rtmutex在设计上更加注重实时性能,采用了更复杂的算法和数据结构,以减少等待时间和提高锁的响应速度。 这使得rtmutex在实时系统中能够更好地满足严格的时间约束。

  2. 高吞吐量:rtmutex在处理高并发场景时能够提供更高的吞吐量,即能够更快地处理大量的并行请求。

  3. 嵌套支持:rtmutex支持嵌套锁的获取和释放,可以在同一线程中多次获取和释放锁。

需要注意的是,rtmutex仅在启用了实时调度策略(如SCHED_FIFO、SCHED_RR)的情况下才可用,而普通的互斥锁(mutex)则适用于所有调度策略。 实时互斥锁的设计和实现与实时调度策略密切相关。 为了确保实时互斥锁(rtmutex)的正确使用和可靠性,只有在启用了实时调度策略(如SCHED_FIFO、SCHED_RR)的情况下,rtmutex才可用。 这样可以保证实时任务在获取和释放rtmutex时能够获得可预测的行为和性能,满足实时系统的要求。

补充一些关于实时调度策略的说明。

首先需要编译的内核是支持抢占的,这个应该基本都默认是抢占的,不过,抢占式调度策略中,还是有细分的(”Preemption Model”), 最高级别的抢占是 PREEMPT_RT。参考内核源码文件/kernel/Kconfig.preempt简要说明

常见的调度策略:SCHED_NORMAL(SCHED_OTHER)属于分时调度策略,SCHED_FIFOSCHED_RR属于实时调度策略。这两类是独立管理的, rt_mutex仅能在实时调度策略的线程中使用。一般的用户程序,由CFS调度器管理,属于分时调度策略,是无法使用的,一般内核的线程可能会使用实时调度策略,可以使用。 实时调度策略的程序使用静态优先级(RTPRIO),分时调度策略的程序使用动态优先级(nice值)。

如何查看系统的线程的调度策略

  • 使用命令sudo ps -eo pid,ppid,cmd,rtprio,ni,policy可以查看系统中正在运行的进程和它们的调度策略信息。 其中,policy列显示了每个进程的调度策略。常见的调度策略包括SCHED_OTHER(CFS调度策略)、SCHED_FIFO(FIFO实时调度策略)和SCHED_RR(Round Robin实时调度策略)。

  • 使用命令chrt -m可以列出可用的调度策略。这个命令将显示系统支持的调度策略列表。

相关接口

实时互斥量相关接口头文件,内核源码的 /include/linux/rtmutex.h 文件。

初始化

1
2
3
4
5
6
7
8
#define DEFINE_RT_MUTEX(mutexname) \
	struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname)

#define rt_mutex_init(mutex) \
do { \
	static struct lock_class_key __key; \
	__rt_mutex_init(mutex, __func__, &__key); \
} while (0)

相关加解锁操作

1
2
3
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);
extern void rt_mutex_unlock(struct rt_mutex *lock);

补充

Linux实时补丁即将合并进Linux 5.3

Ingo Molnar-实时补丁

为了能并入主流内核,Ingo Molnar的实时补丁也采用了非常灵活的策略,它支持四种抢占模式:

  1. No Forced Preemption (Server),这种模式等同于没有使能抢占选项的标准内核,主要适用于科学计算等服务器环境。

  2. Voluntary Kernel Preemption (Desktop),这种模式使能了自愿抢占,但仍然失效抢占内核选项,它通过增加抢占点缩减了抢占延迟,因此适用于一些需要较好的响应性的环境,如桌面环境,当然这种好的响应性是以牺牲一些吞吐率为代价的。

  3. Preemptible Kernel (Low-Latency Desktop),这种模式既包含了自愿抢占,又使能了可抢占内核选项,因此有很好的响应延迟,实际上在一定程度上已经达到了软实时性。它主要适用于桌面和一些嵌入式系统,但是吞吐率比模式2更低。

  4. Complete Preemption (Real-Time),这种模式使能了所有实时功能,因此完全能够满足软实时需求,它适用于延迟要求为100微秒或稍低的实时系统。

实现实时是以牺牲系统的吞吐率为代价的,因此实时性越好,系统吞吐率就越低。

This post is licensed under CC BY 4.0 by the author.

misc杂项设备

内核-seqlock(顺序锁)