内核实时互斥量
实时互斥量解决什么问题
实时互斥量是内核支持的另一种形式的互斥量(mutex)。需要在内核编译时通过配置选项CONFIG_RT_MUTEX
显式启用。与普通的互斥量相比,它们实现了优先级继承(priority inheritance),该特性可用于解决(或在最低限度上缓解)优先级反转的影响。
什么是优先级反转
内核mutex存在优先级反转的问题。什么是优先级反转?
考虑一种情况,系统上有两个进程运行:进程A优先级高,进程C优先级低。假定进程C已经获取了一个互斥量,正在所保护的临界区中运行,且在短时间内不打算退出。但在进程C进入临界区之后不久,进程A也试图获取该互斥量。由于进程C已经获取到该互斥量,因而进程A必须等待。这导致高优先级的进程A等待低优先级的进程C。
如果有第3个进程B,优先级介于进程A和进程C之间,情况会更加糟糕。假定进程C仍然持有锁,进程A在等待。 现在进程B开始运行。由于它的优先级高于进程C,因此可以抢占进程C。 但它实际上也抢占了进程A,尽管进程A的优先级高于进程B。 如果进程B继续运行,那么它可以让进程A等待更长时间,因为进程C被进程B抢占,所以它只能更慢地完成其操作。因此看起来仿佛进程B的优先级高于进程A一样。这种糟糕的情况称为无限制优先级反转(unbounded priority inversion)。
如何解决优先级反转
优先级反转的问题可以通过优先级继承解决。
继续使用之前的例子。如果高优先级进程(进程A)阻塞在互斥量上,该互斥量当前由低优先级进程(进程C)持有,实现优先级继承后,那么进程C的优先级临时提高到进程A的优先级。 如果进程B现在开始运行,只能得到与进程A竞争情况下的CPU时间,从而理顺了优先级的问题。这样进程C就不会被进程B抢占,从而能尽快运行从而释放锁,缩短了进程A的阻塞时间。
数据结构
/**
* The rt_mutex structure
*
* @wait_lock: spinlock to protect the structure
* @waiters: rbtree root to enqueue waiters in priority order
* @waiters_leftmost: top waiter
* @owner: the mutex owner
*/
struct rt_mutex {
raw_spinlock_t wait_lock;
struct rb_root waiters;
struct rb_node *waiters_leftmost;
struct task_struct *owner;
#ifdef CONFIG_DEBUG_RT_MUTEXES
int save_state;
const char *name, *file;
int line;
void *magic;
#endif
};
**<font style="color:rgb(64, 64, 64);">wait_lock</font>**
:一个自旋锁,用于保护该结构体的其它成员。**<font style="color:rgb(64, 64, 64);">waiters</font>**
:一个红黑树的根节点,用于按任务优先级顺序排列等待锁的任务。- 红黑树是一种自平衡二叉排序树,能够高效地进行插入、删除和查找操作,确保等待任务按照优先级有序排队。
**<font style="color:rgb(64, 64, 64);">waiters_leftmost</font>**
:指向优先级最高的等待任务(即红黑树中最左边的节点)。**<font style="color:rgb(64, 64, 64);">owner</font>**
:指向当前持有锁的任务结构体(<font style="color:rgb(64, 64, 64);">task_struct*</font>
)。<font style="color:rgb(64, 64, 64);">task_struct*</font>
是按 8 字节对齐的(64 位系统),指针的最低3位始终为 0,最低为可以用来存储标志位<font style="color:rgb(64, 64, 64);">RT_MUTEX_HAS_WAITERS</font>
(表示等待队列中是否有任务)。
rw_mutex
操作方法
初始化实时互斥量
初始化静态rt_mutex
的方法如下:
DEFINE_RT_MUTEX(mutexname);
运行时动态初始化rt_mutex
的方法如下:
rt_mutex_init(mutex);
申请实时互斥量
申请实时互斥量的函数如下。
(1)void rt_mutex_lock(struct rt_mutex *lock);
申请实时互斥量,如果锁被占有,进程深度睡眠。
(2)int rt_mutex_lock_interruptible(struct rt_mutex *lock);
申请实时互斥量,如果锁被占有,进程轻度睡眠。
(3)int rt_mutex_timed_lock(struct rt_mutex *lock, struct hrtimer_sleeper *timeout);
申请实时互斥量,如果锁被占有,进程睡眠等待一段时间。
(4)int rt_mutex_trylock(struct rt_mutex *lock);
非阻塞方式申请实时互斥量,如果申请成功,返回1;如果锁被其他进程占有,进程不等待,返回0。
释放实时互斥量
释放实时互斥量的函数如下:
void rt_mutex_unlock(struct rt_mutex *lock);
rt_mutex_lock
void __sched rt_mutex_lock(struct rt_mutex *lock)
{
might_sleep();
rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, rt_mutex_slowlock);
}
这是获取实时互斥锁的入口函数,主要完成以下操作:
**<font style="color:rgb(64, 64, 64);">might_sleep()</font>**
:这是一个宏,提示内核当前函数可能会睡眠。这对于调试和确保函数不会在不可睡眠的原子上下文中调用非常重要。- 如果当前代码运行在非睡眠环境(如中断上下文),会触发 调试警告,防止死锁。
- 实时互斥量可能导致进程阻塞,因此不能在中断上下文或持有自旋锁的情况下调用。
**<font style="color:rgb(64, 64, 64);">rt_mutex_fastlock</font>**
:该函数尝试通过快速路径获取锁。如果快速路径失败,则执行慢速路径。
快速路径
/*
* debug aware fast / slowpath lock,trylock,unlock
*
* The atomic acquire/release ops are compiled away, when either the
* architecture does not support cmpxchg or when debugging is enabled.
*/
static inline int
rt_mutex_fastlock(struct rt_mutex *lock, int state,
int (*slowfn)(struct rt_mutex *lock, int state,
struct hrtimer_sleeper *timeout,
enum rtmutex_chainwalk chwalk))
{
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
return 0;
return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK);
}
该函数尝试通过快速路径获取锁:
**<font style="color:rgb(64, 64, 64);">rt_mutex_cmpxchg_acquire</font>**
:该函数尝试使用原子比较交换操作(compare-and-swap)获取锁。如果成功,则返回 0,表示锁已被获取。- ※ 如果通过快速路径获取到锁,则
<font style="color:rgb(64, 64, 64);">owner</font>
设置为当前任务。最低位的等待队列标志位为0,等待队列中没有任务。
- ※ 如果通过快速路径获取到锁,则
**<font style="color:rgb(64, 64, 64);">slowfn</font>**
:如果快速路径失败,则调用慢速路径函数(<font style="color:rgb(64, 64, 64);">rt_mutex_slowlock</font>
)来处理锁的获取。
慢速路径
/*
* Slow path lock function:
*/
static int __sched
rt_mutex_slowlock(struct rt_mutex *lock, int state,
struct hrtimer_sleeper *timeout,
enum rtmutex_chainwalk chwalk)
{
struct rt_mutex_waiter waiter;
unsigned long flags;
int ret = 0;
rt_mutex_init_waiter(&waiter);
/*
* Technically we could use raw_spin_[un]lock_irq() here, but this can
* be called in early boot if the cmpxchg() fast path is disabled
* (debug, no architecture support). In this case we will acquire the
* rtmutex with lock->wait_lock held. But we cannot unconditionally
* enable interrupts in that early boot case. So we need to use the
* irqsave/restore variants.
*/
raw_spin_lock_irqsave(&lock->wait_lock, flags);
/* Try to acquire the lock again: */
if (try_to_take_rt_mutex(lock, current, NULL)) {
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
return 0;
}
set_current_state(state);
/* Setup the timer, when timeout != NULL */
if (unlikely(timeout))
hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
ret = task_blocks_on_rt_mutex(lock, &waiter, current, chwalk);
if (likely(!ret))
/* sleep on the mutex */
ret = __rt_mutex_slowlock(lock, state, timeout, &waiter);
if (unlikely(ret)) {
__set_current_state(TASK_RUNNING);
if (rt_mutex_has_waiters(lock))
remove_waiter(lock, &waiter);
rt_mutex_handle_deadlock(ret, chwalk, &waiter);
}
/*
* try_to_take_rt_mutex() sets the waiter bit
* unconditionally. We might have to fix that up.
*/
fixup_rt_mutex_waiters(lock);
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
/* Remove pending timer: */
if (unlikely(timeout))
hrtimer_cancel(&timeout->timer);
debug_rt_mutex_free_waiter(&waiter);
return ret;
}
该函数处理锁获取的慢速路径,关键步骤如下:
- 初始化一个等待者(
<font style="color:rgb(64, 64, 64);">rt_mutex_init_waiter()</font>
)。 - 使用
<font style="color:rgb(64, 64, 64);">raw_spin_lock_irqsave()</font>
获取<font style="color:rgb(64, 64, 64);">lock->wait_lock</font>
,以保证中断安全。 - 使用
<font style="color:rgb(64, 64, 64);">try_to_take_rt_mutex</font>
尝试再次获取锁。设置<font style="color:rgb(64, 64, 64);">owner</font>
中的最低为标志位<font style="color:rgb(64, 64, 64);">RT_MUTEX_HAS_WAITERS</font>
(表示有任务等待锁)。如果成功,则释放自旋锁并返回。 - 如果无法获取锁,则将设置当前任务状态(如
<font style="color:rgb(64, 64, 64);">TASK_UNINTERRUPTIBLE</font>
)。 - 如果设置了超时(
<font style="color:rgb(64, 64, 64);">timeout</font>
不为<font style="color:rgb(64, 64, 64);">NULL</font>
),则启动一个高分辨率计时器hrtimer,用于在指定时间后唤醒任务。<font style="color:rgb(64, 64, 64);">timeout</font>
主要是针对<font style="color:rgb(64, 64, 64);">rt_mutex_timed_lock</font>
,<font style="color:rgb(64, 64, 64);">timeout</font>
过期后唤醒当前任务,避免任务长时间阻塞。
- 使用
<font style="color:rgb(64, 64, 64);">task_blocks_on_rt_mutex</font>
将任务添加到等待队列中,在这里实现优先级继承。- 此函数是实现优先级继承的核心函数。
- 如果当前锁的持有者的优先级等待队列中优先级最高的任务(红黑树的最左侧节点),则临时提高锁持有者的优先级到该任务的优先级。
- 通过
<font style="color:rgb(64, 64, 64);">rt_mutex_adjust_prio</font>
提升锁持有者的优先级。 - 记录锁持有者的临时优先级提升状态,以便在释放互斥锁后恢复其原始优先级。
- 执行
<font style="color:rgb(64, 64, 64);">__rt_mutex_slowlock</font>
进行进一步的等待处理, 当前任务会睡眠阻塞。<font style="color:rgb(64, 64, 64);">try_to_take_rt_mutex</font>
,设置<font style="color:rgb(64, 64, 64);">owner</font>
中的最低为标志位<font style="color:rgb(64, 64, 64);">RT_MUTEX_HAS_WAITERS</font>
(表示有任务等待锁)。
- 当前任务被**唤醒(获取到锁,得到调度)**时:
- 恢复当前任务的状态(
TASK_RUNNING
)。 - 从等待队列中移除当前任务。
- 处理死锁(
rt_mutex_handle_deadlock()
)。 - 调整
owner
最低位标志(fixup_rt_mutex_waiters()
),表明等待队列中是否有任务(RT_MUTEX_HAS_WAITERS
)。
- 恢复当前任务的状态(
- 释放自旋锁
<font style="color:rgb(64, 64, 64);">lock->wait_lock</font>
,并取消任何活动的定时器。
rt_mutex_unlock
/**
* rt_mutex_unlock - unlock a rt_mutex
*
* @lock: the rt_mutex to be unlocked
*/
void __sched rt_mutex_unlock(struct rt_mutex *lock)
{
rt_mutex_fastunlock(lock, rt_mutex_slowunlock);
}
EXPORT_SYMBOL_GPL(rt_mutex_unlock);
<font style="color:rgb(64, 64, 64);">rt_mutex_unlock</font>
的主要功能是:
- 释放锁:将锁的持有者设置为
<font style="color:rgb(64, 64, 64);">NULL</font>
,表示锁已被释放。 - 处理快速路径和慢速路径:
- 快速路径:如果锁的等待队列为空,则直接释放锁。
- 慢速路径:如果锁的等待队列不为空,则需要唤醒下一个等待者,并恢复持有锁任务的优先级。
- 唤醒下一个等待者:如果锁的等待队列中有任务在等待,则唤醒优先级最高的等待者。
快速路径
static inline void
rt_mutex_fastunlock(struct rt_mutex *lock,
bool (*slowfn)(struct rt_mutex *lock,
struct wake_q_head *wqh))
{
DEFINE_WAKE_Q(wake_q);
if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
return;
if (slowfn(lock, &wake_q))
rt_mutex_postunlock(&wake_q);
}
- 快速路径:
- 使用
<font style="color:rgb(64, 64, 64);">rt_mutex_cmpxchg_release</font>
尝试将锁的持有者从当前任务(<font style="color:rgb(64, 64, 64);">current</font>
)设置为<font style="color:rgb(64, 64, 64);">NULL</font>
。- 如果
<font style="color:rgb(64, 64, 64);">current</font>
和<font style="color:rgb(64, 64, 64);">lock->owner</font>
一致(等待队列为空),则直接释放锁。
- 如果
- 如果成功,则直接返回,表示锁已被释放。
- 使用
- 慢速路径:
- 如果快速路径失败(例如,锁的等待队列不为空),则调用
<font style="color:rgb(64, 64, 64);">slowfn</font>
(即<font style="color:rgb(64, 64, 64);">rt_mutex_slowunlock</font>
)处理慢速路径。 - 如果
<font style="color:rgb(64, 64, 64);">slowfn</font>
返回<font style="color:rgb(64, 64, 64);">true</font>
,则调用<font style="color:rgb(64, 64, 64);">rt_mutex_postunlock</font>
唤醒等待队列中的任务。
- 如果快速路径失败(例如,锁的等待队列不为空),则调用
慢速路径
/*
* Slow path to release a rt-mutex.
*
* Return whether the current task needs to call rt_mutex_postunlock().
*/
static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
struct wake_q_head *wake_q)
{
unsigned long flags;
/* irqsave required to support early boot calls */
raw_spin_lock_irqsave(&lock->wait_lock, flags);
debug_rt_mutex_unlock(lock);
/*
* We must be careful here if the fast path is enabled. If we
* have no waiters queued we cannot set owner to NULL here
* because of:
*
* foo->lock->owner = NULL;
* rtmutex_lock(foo->lock); <- fast path
* free = atomic_dec_and_test(foo->refcnt);
* rtmutex_unlock(foo->lock); <- fast path
* if (free)
* kfree(foo);
* raw_spin_unlock(foo->lock->wait_lock);
*
* So for the fastpath enabled kernel:
*
* Nothing can set the waiters bit as long as we hold
* lock->wait_lock. So we do the following sequence:
*
* owner = rt_mutex_owner(lock);
* clear_rt_mutex_waiters(lock);
* raw_spin_unlock(&lock->wait_lock);
* if (cmpxchg(&lock->owner, owner, 0) == owner)
* return;
* goto retry;
*
* The fastpath disabled variant is simple as all access to
* lock->owner is serialized by lock->wait_lock:
*
* lock->owner = NULL;
* raw_spin_unlock(&lock->wait_lock);
*/
while (!rt_mutex_has_waiters(lock)) {
/* Drops lock->wait_lock ! */
if (unlock_rt_mutex_safe(lock, flags) == true)
return false;
/* Relock the rtmutex and try again */
raw_spin_lock_irqsave(&lock->wait_lock, flags);
}
/*
* The wakeup next waiter path does not suffer from the above
* race. See the comments there.
*
* Queue the next waiter for wakeup once we release the wait_lock.
*/
mark_wakeup_next_waiter(wake_q, lock);
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
return true; /* call rt_mutex_postunlock() */
}
慢速路径释放锁的关键点:
- 处理无等待者的情况:
- 如果锁的等待队列为空,则调用
<font style="color:rgb(64, 64, 64);">unlock_rt_mutex_safe</font>
安全地释放锁,将<font style="color:rgb(64, 64, 64);">owner</font>
设置为<font style="color:rgb(64, 64, 64);">NULL</font>
。 - 如果
<font style="color:rgb(64, 64, 64);">unlock_rt_mutex_safe</font>
成功,则返回<font style="color:rgb(64, 64, 64);">false</font>
,表示不需要调用<font style="color:rgb(64, 64, 64);">rt_mutex_postunlock</font>
唤醒下一个等待者。 - 如果
<font style="color:rgb(64, 64, 64);">unlock_rt_mutex_safe</font>
失败,则重试。
- 如果锁的等待队列为空,则调用
- 处理有等待者的情况:
- 如果锁的等待队列不为空,则调用
<font style="color:rgb(64, 64, 64);">mark_wakeup_next_waiter</font>
将下一个等待者(等待队列中优先级最高的任务)加入唤醒队列。在<font style="color:rgb(64, 64, 64);">mark_wakeup_next_waiter</font>
中,恢复锁持有者的优先级。 - 返回
<font style="color:rgb(64, 64, 64);">true</font>
,表示需要调用<font style="color:rgb(64, 64, 64);">rt_mutex_postunlock</font>
唤醒下一个等待者。 - 通过
<font style="color:rgb(64, 64, 64);">rt_mutex_postunlock</font>
唤醒等待队列中的任务。
- 如果锁的等待队列不为空,则调用
rt_mutex
和mutex
比较
特性 | 普通互斥量 (**mutex** ) | 实时互斥量 (**rt_mutex** ) |
---|---|---|
设计目标 | 通用、高性能 | 实时性、确定性(高优先级任务优先) |
优先级继承 | 不支持 | 支持 |
实现机制 | 乐观自旋、快速路径/慢速路径 | 优先级继承、死锁检测、快速路径/慢速路径 |
性能 | 低开销,适用于锁争用较少的场景 | 较高开销,适用于实时任务 |
适用场景 | 普通内核代码路径 | 实时任务 |
代码复杂度 | 相对简单 | 较为复杂 |
锁的争用处理 | 乐观自旋、等待队列(list) | 优先级继承、等待队列(rbtree) |
普通互斥量 (<font style="color:rgb(64, 64, 64);">mutex</font>
) 更适合通用的内核代码路径,而实时互斥量 (<font style="color:rgb(64, 64, 64);">rt_mutex</font>
) 则适用于实时任务和对锁获取时间有严格要求的场景。
用户空间实时互斥量
在 Linux 用户空间,实时互斥量(Real-Time Mutex)的实现通常依赖于 POSIX 线程库(<font style="color:rgb(64, 64, 64);">pthread</font>
)提供的实时扩展功能。POSIX 实时扩展(POSIX Real-Time Extensions)定义了一系列用于实时编程的接口,其中包括实时互斥量(<font style="color:rgb(64, 64, 64);">pthread_mutex_t</font>
)和优先级继承机制。以下是 Linux 用户空间实时互斥量的实现方式和关键点:
POSIX 实时互斥量
POSIX 标准定义了实时互斥量的接口,可以通过 <font style="color:rgb(64, 64, 64);">pthread_mutex_t</font>
和相关函数来实现。实时互斥量的关键特性包括:
- 优先级继承:支持优先级继承机制,避免优先级反转问题。
- 超时机制:支持带超时的锁获取操作。
- 递归锁:支持递归锁(同一线程多次加锁)。
关键函数
<font style="color:rgb(64, 64, 64);">pthread_mutex_init</font>
:初始化互斥量。<font style="color:rgb(64, 64, 64);">pthread_mutex_lock</font>
:加锁。<font style="color:rgb(64, 64, 64);">pthread_mutex_unlock</font>
:解锁。<font style="color:rgb(64, 64, 64);">pthread_mutex_trylock</font>
:尝试加锁(非阻塞)。<font style="color:rgb(64, 64, 64);">pthread_mutex_timedlock</font>
:带超时的加锁操作。<font style="color:rgb(64, 64, 64);">pthread_mutex_destroy</font>
:销毁互斥量。
实时互斥量属性
通过 <font style="color:rgb(64, 64, 64);">pthread_mutexattr_t</font>
可以设置互斥量的属性,包括:
- 协议(Protocol):
<font style="color:rgb(64, 64, 64);">PTHREAD_PRIO_NONE</font>
:无优先级继承。<font style="color:rgb(64, 64, 64);">PTHREAD_PRIO_INHERIT</font>
:支持优先级继承。<font style="color:rgb(64, 64, 64);">PTHREAD_PRIO_PROTECT</font>
:支持优先级上限(Priority Ceiling)。
- 类型(Type):
<font style="color:rgb(64, 64, 64);">PTHREAD_MUTEX_NORMAL</font>
:普通互斥量。<font style="color:rgb(64, 64, 64);">PTHREAD_MUTEX_RECURSIVE</font>
:递归互斥量。<font style="color:rgb(64, 64, 64);">PTHREAD_MUTEX_ERRORCHECK</font>
:错误检查互斥量。
优先级继承的实现
在用户空间,优先级继承是通过 <font style="color:rgb(64, 64, 64);">pthread_mutexattr_setprotocol</font>
设置 <font style="color:rgb(64, 64, 64);">PTHREAD_PRIO_INHERIT</font>
协议来实现的。当高优先级任务被低优先级任务持有的锁阻塞时,低优先级任务会继承高优先级任务的优先级,直到它释放锁。
优先级继承示例
// gcc -o rt_mutex_test rt_mutex_test.c -lpthread -lrt
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <sys/time.h>
#include <time.h>
pthread_mutex_t mutex;
struct timeval start, end;
void* thread1_func(void* arg) {
pthread_mutex_lock(&mutex);
printf("线程1: 持有 mutex(优先级继承)\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("线程1: 释放 mutex\n");
return NULL;
}
void* thread2_func(void* arg) {
struct timespec start, current;
clock_gettime(CLOCK_MONOTONIC, &start);
printf("线程2: 开始忙等待 2 秒\n");
while (1) {
clock_gettime(CLOCK_MONOTONIC, ¤t);
double elapsed = (current.tv_sec - start.tv_sec) +
(current.tv_nsec - start.tv_nsec) / 1e9;
if (elapsed >= 2.0)
break;
}
printf("线程2: 结束忙等待\n");
return NULL;
}
void* thread3_func(void* arg) {
sleep(1);
gettimeofday(&start, NULL);
printf("线程3: 尝试获取 mutex...\n");
pthread_mutex_lock(&mutex);
gettimeofday(&end, NULL);
printf("线程3: 获取到 mutex\n");
pthread_mutex_unlock(&mutex);
double block_time = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
printf("线程3: 被阻塞时间 = %.2f ms\n", block_time);
return NULL;
}
int main() {
pthread_t thread1, thread2, thread3;
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&mutex, &mutex_attr);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
struct sched_param param;
pthread_attr_t attr1, attr2, attr3;
pthread_attr_init(&attr1);
pthread_attr_init(&attr2);
pthread_attr_init(&attr3);
pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpuset);
pthread_attr_setaffinity_np(&attr2, sizeof(cpu_set_t), &cpuset);
pthread_attr_setaffinity_np(&attr3, sizeof(cpu_set_t), &cpuset);
param.sched_priority = 10;
pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
pthread_attr_setschedparam(&attr1, ¶m);
param.sched_priority = 20;
pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
pthread_attr_setschedparam(&attr2, ¶m);
param.sched_priority = 30;
pthread_attr_setschedpolicy(&attr3, SCHED_FIFO);
pthread_attr_setschedparam(&attr3, ¶m);
pthread_create(&thread1, &attr1, thread1_func, NULL);
pthread_create(&thread2, &attr2, thread2_func, NULL);
pthread_create(&thread3, &attr3, thread3_func, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
pthread_mutex_destroy(&mutex);
pthread_mutexattr_destroy(&mutex_attr);
return 0;
}
注意事项
- 实时调度策略:为了确保实时性,任务需要使用实时调度策略(如
<font style="color:rgb(64, 64, 64);">SCHED_FIFO</font>
或<font style="color:rgb(64, 64, 64);">SCHED_RR</font>
)。 - 权限:使用实时调度策略和设置高优先级可能需要 root 权限。
- 优先级继承的限制:优先级继承机制依赖于内核的支持,因此在用户空间的实现可能会受到内核版本和配置的限制。
总结
在 Linux 用户空间,实时互斥量的实现主要依赖于 POSIX 线程库的实时扩展功能。通过 <font style="color:rgb(64, 64, 64);">pthread_mutex_t</font>
和 <font style="color:rgb(64, 64, 64);">pthread_mutexattr_t</font>
,可以设置优先级继承协议、超时机制等特性,从而实现实时互斥量。优先级继承机制确保了高优先级任务能够尽快获取锁,避免优先级反转问题。
参考资料
- Professional Linux Kernel Architecture,Wolfgang Mauerer
- Linux内核深度解析,余华兵
- Linux设备驱动开发详解,宋宝华
- linux kernel 4.12