1、概述
互斥体用于控制嵌入式操作系统中多个任务对共享资源的访问。这类资源在任意时刻最多容许一个任务访问。前面提到的信号量机制也能够实现这种功能。但互斥体相比信号量还提供了优先级反转问题的解决方法。
优先级反转问题,简而言之就是低优先级的任务占有了高优先级任务需要的资源,导致高优先级的任务阻塞。这样优先级高的任务反而不能得到执行,而执行的是低优先级任务。一般提到的解决方案有两种:优先级继承协议、天花板优先级协议。前者指当有高优先级的任务申请资源时,当前占有资源的低优先级任务被抬升到高优先级任务相同的优先级。后者,则是设定了等于或大于所有申请资源的任务的优先级。当高优先级任务申请资源时,当前占有资源的低优先级任务的优先级被抬升到设定的最高优先级。
eos中使用的天花板优先级协议。并且支持任务多次申请同一互斥体。
2、互斥体的实现
1)、互斥体结构
互斥体的结构示意图如下:
互斥体的结构定义如下:
typedef struct _mutex_t /* 互斥体结构 */
{
? uint8 ceil_prio; /* 互斥体拥有的优先级*/
? uint8 ref; /* 互斥体引用次数 */
? task_t owner; /* 互斥体占有者 */
? uint8 owner_prio; /* 互斥体占有者优先级*/
? mlist_t wait_list; /* 互斥体阻塞队列 */
? uint8 cflag; /* 互斥体控制标志 */
}* mutex_t;
其中.owner, owner_prio分别保存互斥体占有任务的指针和优先级。
.wait_list为阻塞队列。当互斥体已被占有时,其它申请互斥体的任务将进入该队列
.ref 为互斥体被当前任务占有的次数。eos允许互斥体被占有者多次申请占有。
.ceil_prio保存天花板优先级协议中指定的最高优先级。
另外,提供mqueue_info_t结构用于查询互斥体信息。结构如下:
typedef struct _mutex_info_t /* 互斥体消息结构 */
{
? uint8 ceil_prio; /* 互斥体拥有的优先级 */
? uint8 ref; /* 互斥体引用次数 */
? task_t owner; /* 互斥体占有者 */
? uint8 owner_prio; /* 互斥体占有者优先级 */
? uint8 cflag; /* 互斥体控制标志 */
}mutex_info_t;
2)、互斥体的接口函数:
主要包含互斥体的创建、销毁、申请、释放、查询操作。列表如下:
uint8 mutex_create( mutex_t * mutex, uint8 prio ); /* 创建一个互斥体 */
uint8 mutex_destroy( mutex_t mutex ); /* 销毁一个互斥体 */
uint8 mutex_request( mutex_t mutex, uint16 time_out ); /* 等待互斥体 */
uint8 mutex_try_request( mutex_t mutex ); /* 非阻塞等待互斥体 */
uint8 mutex_release( mutex_t mutex ); /* 释放互斥体 */
uint8 mutex_info( mutex_t mutex, mutex_info_t * info ); /* 查询互信息 */
各函数的操作流程如下:
mutex_create()操作流程:
参数检查
调用mpool_get()分配互斥体控制块结构struct _mutex_t
分配成功
是,
初始化控制块结构内部各域
初始化阻塞队列
mutex_info()操作流程:
参数检查
复制互斥体控制块信息至mutex_info_t结构中
mutex_destroy()操作流程:
参数检查
互斥体控制块.ref > 0,即互斥体已被占用
是,
检查cflag标志,是否已使能了优先级的调整
是
任务是否在处于多级队列中
是,
将任务从多级队列中移除
恢复原来的任务优先级
再将任务插入多级队列
否,?
恢复原来的优先级
销毁互斥体的是否为当前任务
是,
修改OSPrioCur为任务原来的优先级
调用event_mlist_del()销毁互斥体的阻塞队列
调用scheduler()进行调度
调用mpool_free()回收互斥体控制块
注:如果有互斥体已经做了优先级的调整,则当销毁互斥体时有必要恢复占有者任务的优先级。优先级的恢复不只是修改任务的task_struct结构。如果任务处于多级队列,还有必要将任务从当前处于的单级队列中移除,并移至原有优先级对应的单级队列。由于前面的设计中,即便使处于运行态的任务也处于就绪队列中,因而此时不并需要判定占有的任务是否处于多级队列中,可作统一的处理。对于当前处于单级队列,如只使用单级阻塞队列的信量,只需修改任务的优先级值即可。
mutex_request()操作流程:
参数检查
是否在中断中或调度器上锁时调用
是,立即返回
检查控制块域.ref,是否互斥体已被占有
否,
如果当前任务优先级比互斥体的.ceil-prio值更小,
不合法优先级,退出
锁定当前互斥体,域 . ref++
返回
是,已被占用
占有者是否为当前任务
是,
控制块域.ref++, 退出
否,
申请者优先级是否比占有者的更高
是,
是否已经进行了优先级调整
否,
任务是否处于多级队列
是,
将任务从现有多级队列移除
修改任务优先级为设定的互斥体优先级
再将任务插入多级队列
否,
修改当前任务优先级
调用event_mlist_wait()将当前任务插入互斥体阻塞队列
调用scheduler()切换至其它任务
返回OSTaskCur->wait_state域
返回OS_ERR_OK
注:容许已占有互斥体的任务多次申请占有互斥体。
mqueue_try_request()操作流程:
参数检查
检查控制块域.ref,是否互斥体已被占有
否,
如果当前任务优先级比互斥体的.ceil-prio值更小,
不合法优先级,退出
锁定当前互斥体,域 . ref++
返回
是,已被占用
占有者是否为当前任务
是,
控制块域.ref++, 退出
mutex_release()操作流程:
参数检查
互斥体是否已被占用
否,
立即返回
是,
是否是占有者释放互斥体
否,
释放不合法,返回
是,
控制块.ref减1
.ref值是否为0
是,
检查cflag标志,是否已使能了优先级的调整
是,
将任务从就绪队列中移除
恢复原来的任务优先级
再将任务插入就绪队列
设定互斥体为阻塞队列中最高优先级的任务占有
调用event_mlist_rdy()唤醒阻塞队列最高优先级任务
调用scheduler()进行调度
注:由于互斥体只能被占有者释放,因而在对已使能优先级调整的互斥体进行优先级恢复时,要恢复的优先级的任务必是当前运行的任务,而且任务必处于就绪队列中。这样,对任务所在队列的修改直接变成将任务从就绪队列中某单级队列移至另一单级队列。
3、简单的演示
提供了一个简单的测试实例 app_mutex.c 演示了互斥体的操作。
4、问题与思考
略
五、相关源码
core.c / core.h ---------- 内核支持函数文件
mutex.c/ipc.h ----------- 互斥体实现文件
用户245924 2010-6-5 20:27
tengjingshu_112148725 2009-8-18 21:29
用户1362017 2009-7-27 19:59
用户526022 2009-7-27 18:59