6.2 POSIX线程间通信
使用 Xenomai 的 POSIX skin创建的POSIX线程,在同一进程内共享相同的地址空间,线程间直接共享同步对象,例如信号量,互斥锁,条件变量等等。
但是,Xenomai 不支持 POSIX 标准中的静态初始化。
1. POSIX 标准中的静态初始化
在 POSIX 标准里,为了方便静态互斥锁(mutex)和条件变量(condition variables)的初始化,定义了 PTHREAD_COND_INITIALIZER 和 PTHREAD_MUTEX_INITIALIZER 这两个初始化器。借助这两个初始化器,开发者能够在声明静态互斥锁和条件变量时直接完成初始化,示例如下:
#include <pthread.h>
// 静态初始化互斥锁
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
// 静态初始化条件变量
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
这种静态初始化方式简洁高效,能在程序启动时就完成对象的初始化,避免了额外的函数调用开销。
2. Xenomai POSIX 层的特殊需求
然而,Xenomai POSIX 层由于其自身的特性,需要通过系统调用来初始化互斥锁和条件变量。这意味着上述 POSIX 标准的静态初始化方式在 Xenomai 环境下无法正常工作,因为 PTHREAD_COND_INITIALIZER 和 PTHREAD_MUTEX_INITIALIZER 无法完成 Xenomai 所需的系统调用初始化过程。
面对这个问题,有两种解决方案可供选择,最终Xenomai选择方案二。
- 方案一: 首次调用其他服务时初始化对象
第一种方案是在首次调用与互斥锁或条件变量相关的其他服务(如 pthread_mutex_lock 或 pthread_cond_wait)时,触发对象的初始化操作。但这种方案存在明显的缺陷。以 pthread_mutex_lock 为例,如果在该函数内部调用初始化例程,会引入额外的不确定性。因为初始化过程可能涉及系统调用,这会增加函数执行时间的不可预测性,破坏了用户对 pthread_mutex_lock 这类服务确定性的预期。在实时系统中,这种不确定性是非常致命的,可能会导致系统性能下降甚至出现错误。
- 方案二:要求用户调用初始化服务
第二种方案是要求用户主动调用 pthread_mutex_init 和 pthread_cond_init 函数来完成互斥锁和条件变量的初始化。这种方案的优点在于,用户可以在程序执行的非关键时期进行初始化操作,避免了在关键路径上引入额外的开销和不确定性。虽然这增加了用户的编程负担,但能保证系统的实时性和确定性。
综上,在使用 Xenomai POSIX 层的互斥锁和条件变量时,开发者需要仔细检查代码中所有使用静态初始化器的地方,并将其替换为在非关键时期调用 pthread_mutex_init 和 pthread_cond_init 函数。示例如下:
#include <pthread.h>
// 声明互斥锁和条件变量
pthread_mutex_t my_mutex;
pthread_cond_t my_cond;
int main() {
// 在非关键时期初始化互斥锁
if (pthread_mutex_init(&my_mutex, NULL) != 0) {
// 处理初始化失败的情况
}
// 在非关键时期初始化条件变量
if (pthread_cond_init(&my_cond, NULL) != 0) {
// 处理初始化失败的情况
}
// 后续正常使用互斥锁和条件变量
// ...
// 程序结束时销毁互斥锁和条件变量
pthread_mutex_destroy(&my_mutex);
pthread_cond_destroy(&my_cond);
return 0;
}
通过这种方式,可以确保在 Xenomai 环境下正确使用互斥锁和条件变量,同时保证系统的实时性和确定性。
3. Xenomai 实例
-
互斥锁 mutex :testsuite/clocktest/clocktest.c
root@xeno-demo:~# clocktest
== Testing built-in CLOCK_REALTIME (0)
CPU ToD offset [us] ToD drift [us/s] warps max delta [us]
0 1.1 -0.024 0 0.0 1 1.2 -0.016 0 0.0 2 1.2 0.009 0 0.0 3 1.3 -0.002 0 0.0 -
互斥量和条件变量:demo/posix/cyclictest/cyclictest.c
为什么使用了静态初始化?
static pthread_cond_t refresh_on_max_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t refresh_on_max_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t break_thread_id_lock = PTHREAD_MUTEX_INITIALIZER;