互斥量

互斥量 是实现传统可重入互斥锁的内核对象。互斥量通过确保对资源的互斥访问,使多个线程能够安全地共享关联的硬件或软件资源。

概念

可以定义任意数量的互斥量(仅受可用 RAM 限制)。每个互斥量都通过其内存地址引用。

互斥量具有以下主要属性:

  • 锁定计数,表示持有互斥量的线程已将其锁定的次数。计数为零表示互斥量未锁定。

  • 所属线程,用于标识在互斥量处于锁定状态时持有它的线程。

互斥量必须初始化后才能使用。初始化会将锁定计数设为零。

需要使用共享资源的线程必须先 锁定 关联的互斥量,以获得独占访问权。如果互斥量已被其他线程锁定,请求线程可以选择等待它解锁。

锁定互斥量后,线程可以根据需要安全地使用关联资源。不过,应尽量缩短持锁时间,以免影响其他需要使用该资源的线程。当线程不再需要该资源时,必须 解锁 互斥量,让其他线程能够使用该资源。

互斥量处于锁定状态或有线程正在等待它时,不得释放互斥量或复用其内存。嵌入动态分配对象中的互斥量必须在该对象释放前解锁,否则所属线程仍会跟踪一个位于已不再归其所有的内存中的互斥量。

任意数量的线程都可以同时等待一个已锁定的互斥量。互斥量解锁后,将由优先级最高且等待时间最长的线程将其锁定。

备注

互斥量对象 不能 用于 ISR。

可重入锁定

线程可以再次锁定自己已经锁定的互斥量。因此,线程执行到某处需要访问关联资源时,无论此前是否已锁定该互斥量,都可以先将其锁定。

线程重复锁定一个互斥量后,必须解锁相同的次数,互斥量才会完全解锁并可由其他线程获取。

优先级继承

持有互斥量的线程可以获得 优先级继承。这意味着,当更高优先级的线程开始等待该互斥量时,内核会 临时 提高持锁线程的优先级。所属线程便能以与等待线程相同的优先级运行,更快地完成工作并释放互斥量。互斥量解锁后,解锁线程将优先级恢复到锁定该互斥量之前的级别。

备注

CONFIG_PRIORITY_CEILING 配置选项限制内核通过优先级继承可将线程优先级提高到的最高级别。默认值 -128 表示不设限制。将其设为空闲线程的优先级或更低的优先级可禁用优先级继承。

线程第一次因互斥量而被提升优先级时,其原始优先级会保存在该线程自身中;线程持有的每个互斥量也都会记录在线程专属的列表中。每当更高优先级的线程等待某个互斥量时,内核都会调整所属线程的优先级。当所属线程释放锁,或高优先级的等待线程超时时,内核会重新扫描该线程仍持有的互斥量,并依据仍在等待这些互斥量的线程中的最高优先级重新计算其优先级;若已无等待线程,则恢复到原始优先级。因此,无论以何种顺序解锁所持有的互斥量,结果都正确。

如果互斥量的所属线程本身正在阻塞等待另一个互斥量,优先级提升会沿这条所有权链传播,依次提高所有阻碍等待线程继续执行的线程的优先级。每次尝试加锁时可遍历的链跳数存在由实现定义的上限。超出该上限的所属线程会保留提升前的优先级,直到后续的加锁尝试再次遍历这条链。

实现

定义互斥量

互斥量使用 k_mutex 类型的变量定义,随后必须调用 k_mutex_init() 进行初始化。

以下代码定义并初始化一个互斥量。

struct k_mutex my_mutex;

k_mutex_init(&my_mutex);

也可以使用 K_MUTEX_DEFINE 在编译时定义并初始化互斥量。

以下代码与上面的代码片段效果相同。

K_MUTEX_DEFINE(my_mutex);

锁定互斥量

调用 k_mutex_lock() 可以锁定互斥量。

以下代码延续上面的示例。如果互斥量已被其他线程锁定,则无限期等待,直到它可用。

k_mutex_lock(&my_mutex, K_FOREVER);

以下代码等待互斥量可用,最长等待 100 毫秒;如果互斥量未能变为可用状态,则发出警告。

if (k_mutex_lock(&my_mutex, K_MSEC(100)) == 0) {
    /* mutex successfully locked */
} else {
    printf("Cannot lock XYZ display\n");
}

解锁互斥量

调用 k_mutex_unlock() 可以解锁互斥量。

以下代码延续上面的示例,解锁先前由该线程锁定的互斥量。

k_mutex_unlock(&my_mutex);

使用建议

使用互斥量提供对资源(例如物理设备)的独占访问。

配置选项

相关配置选项:

API 参考

Mutex APIs

Futex API 参考

k_futex 是一种轻量级互斥原语,旨在尽量减少内核介入。无竞争时,操作仅依赖对共享内存的原子访问。k_futex 不是内核对象,不使用内核对象权限管理机制。它们位于用户内存中,任何对该内存具有读写权限的线程都可以使用。

FUTEX APIs

用户模式互斥量 API 参考

sys_mutex 的行为与 k_mutex 几乎完全相同,额外的优势是 sys_mutex 实例可以位于用户内存中。未启用用户模式时,sys_mutex 的行为与 k_mutex 相同。

User mode mutex APIs