互斥锁是实现传统重入互斥体的内核对象。互斥锁允许多个线程通过确保对资源的互斥访问来安全地共享相关的硬件或软件资源。
我正在学习 Zephyr,一个很可能会用到很多物联网设备上的操作系统,如果你也感兴趣,可点此查看帖子zephyr学习笔记汇总。
可以定义任何数量的互斥锁。 每个互斥量都由其内存地址引用。
互斥锁具有以下关键属性:
互斥量在使用之前必须初始化。这将其锁定计数设置为零。
需要使用共享资源的线程必须首先通过锁定关联的互斥锁来获得专用权限才能访问它。 如果该互斥体已被另一个线程锁定,则发出请求的线程可以选择等待该互斥体被解锁。
在锁定互斥锁后,线程可以安全地使用相关资源,只要需要; 但是,尽可能缩短锁定时间以避免对要使用该资源的其他线程产生负面影响,这是公认的较好的做法。 当线程不再需要该资源时,它必须解锁该互斥体以允许其他线程使用该资源。
任何数量的线程可能会同时等待锁定的互斥锁。当互斥锁变为解锁时,它将被等待时间最长的最高优先级线程锁定。
注意:互斥对象不适用于ISR。
一个线程允许锁定已经被它锁定的互斥锁。这使得线程可以在互斥量可能已经或可能未被锁定的期间上访问相关资源。
一个线程重复锁定的互斥锁需要解锁相同次数,才能完全解锁,以便可以由另一个线程声明。
已锁定互斥锁的线程有资格获得优先级继承。这意味着如果更高优先级的线程开始等待互斥量,内核将暂时提升线程的优先级。 这允许拥有线程完成其工作并通过以与等待线程相同的优先级执行来更快速地释放互斥体。 一旦互斥锁被解锁,解锁线程会将其优先级重置为锁定该互斥锁之前的级别。
CONFIG_PRIORITY_CEILING 配置选项限制了由于优先级继承而导致内核可以提高线程优先级的程度。 默认值0允许无限升高。
当两个或更多线程等待由较低优先级线程保持的互斥量时,内核会在每次线程开始等待(或放弃等待)时调整拥有线程的优先级。 当最终解锁互斥锁时,解锁线程的优先级正确地恢复到原来的非高优先级。
当一个线程同时持有两个或多个互斥锁时,内核不完全支持优先级继承。 当所有互斥锁被释放时,这种情况可能导致线程的优先级不会恢复到原来的非高优先级。 建议在不同优先级的线程之间共享多个互斥锁时,线程只锁定一个互斥锁。
互斥锁是使用 struct 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);
使用互斥锁来提供资源(如物理设备)的独占访问权限。
CONFIG_PRIORITY_CEILING
下列互斥锁API,都在 kernel.h 中提供了:
K_MUTEX_DEFINE
k_mutex_init()
k_mutex_lock()
k_mutex_unlock()