信号量(semaphore)用来保护共享资源,临界区访问,同步;可以用于生产者-消费者模式中提供事件通知。
如果信号量计数器最大为1,其值可能为0或1,成为二进制信号量。二进制信号量作用类似互斥量,但信号量不支持所有权,不支持优先级继承。
信号量控制块
Threadx中信号量控制块(SCB)用来保持运行时(run-time)信号量状态的数据结构。
/* Define the semaphore structure utilized by the application. */
typedef struct TX_SEMAPHORE_STRUCT
{
/* Define the semaphore ID used for error checking. */
ULONG tx_semaphore_id;
/* Define the semaphore's name. */
CHAR_PTR tx_semaphore_name;
/* Define the actual semaphore count. A zero means that no semaphore
instance is available. */
ULONG tx_semaphore_count;
/* Define the semaphore suspension list head along with a count of
how many threads are suspended. */
struct TX_THREAD_STRUCT *tx_semaphore_suspension_list;
ULONG tx_semaphore_suspended_count;
/* Define the created list next and previous pointers. */
struct TX_SEMAPHORE_STRUCT
*tx_semaphore_created_next,
*tx_semaphore_created_previous;
TX_THREAD *sema_last_owner;
} TX_SEMAPHORE;
域 | 意义 |
---|---|
tx_semaphore_id | 信号量ID |
tx_semaphore_name | 信号量名字指针 |
tx_semaphore_count | 信号量计数器 |
tx_semaphore_suspension_list | 信号量挂起队列 |
tx_semaphore_suspended_count | 信号量挂起队列中元素个数 |
tx_semaphore_created_next | 指向下一个信号量指针 |
tx_semaphore_created_previous | 指向前一个信号量指针 |
sema_last_owner | 最后一个获取信号量线程指针 |
信号量队列
系统中所有信号量控制块挂载一个双向链表_tx_semaphore_created_ptr中,tx_semaphore_created_next 指向下一个信号量指针,tx_semaphore_created_previous指向前一个信号量指针。
信号量API
函数 | 描述 |
---|---|
_tx_semaphore_create | 创建信号量 |
_tx_semaphore_delete | 删除信号量 |
_tx_semaphore_get | 申请信号量 |
_tx_semaphore_info_get | 获取信号量信息 |
_tx_semaphore_prioritize | 调整信号量挂起队列,使优先级最高线程在最前面 |
_tx_semaphore_put | 释放信号量 |
优先级翻转
优先级翻转是指较低优先级已经获得信号量,这时较高优先级线程也需要获得的这个信号量时,较高优先级线程会被挂起,等待信号量释放。如果这时中等优先级任务抢占了低优先级任务,就出现了低优先级任务先执行,高优先级任务在等待,而且时间也变得不确定。
解决信号量导致的优先级翻转方法:开发人员通过合理设置优先级避免优先级翻转,或暂时提高拥有信号量的线程优先级避免优先级翻转。
互斥量支持优先级继承功能来提高拥有信号量的线程优先级。
信号量不支持优先级继承功能来提高拥有信号量的线程优先级。
信号量创建_tx_semaphore_create
_tx_semaphore_create用来创建信号量,入参为信号量指针,信号量名字指针,信号量计数器初值。
UINT _tx_semaphore_create(TX_SEMAPHORE *semaphore_ptr,
CHAR *name_ptr, ULONG initial_count)
{
TX_INTERRUPT_SAVE_AREA
TX_SEMAPHORE *tail_ptr; /* Working semaphore pointer */
/* Setup the basic semaphore fields. */
#def 设置初始化值
semaphore_ptr -> tx_semaphore_name = name_ptr;
semaphore_ptr -> tx_semaphore_count = initial_count;
semaphore_ptr -> tx_semaphore_suspension_list = TX_NULL;
semaphore_ptr -> tx_semaphore_suspended_count = 0;
/* Disable interrupts to place the semaphore on the created list. */
TX_DISABLE
/* Setup the semaphore ID to make it valid. */
semaphore_ptr -> tx_semaphore_id = TX_SEMAPHORE_ID;
/* Place the semaphore on the list of created semaphores. First,
check for an empty list. */
#def 插入_tx_semaphore_created_ptr list尾部
if (_tx_semaphore_created_ptr)
{
/* Pickup tail pointer. */
tail_ptr = _tx_semaphore_created_ptr -> tx_semaphore_created_previous;
/* Place the new semaphore in the list. */
_tx_semaphore_created_ptr -> tx_semaphore_created_previous = semaphore_ptr;
tail_ptr -> tx_semaphore_created_next = semaphore_ptr;
/* Setup this semaphore's next and previous created links. */
semaphore_ptr -> tx_semaphore_created_previous = tail_ptr;
semaphore_ptr -> tx_semaphore_created_next = _tx_semaphore_created_ptr;
}
else
{
/* The created semaphore list is empty. Add semaphore to empty list. */
_tx_semaphore_created_ptr = semaphore_ptr;
semaphore_ptr -> tx_semaphore_created_next = semaphore_ptr;
semaphore_ptr -> tx_semaphore_created_previous = semaphore_ptr;
}
/* Increment the number of semaphores created counter. */
_tx_semaphore_created_count++;
/* Restore interrupts. */
TX_RESTORE
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}
删除信号量_tx_semaphore_delete
删除信号量,如果tx_semaphore_suspension_list挂起队列中有线程,需要恢复线程
UINT _tx_semaphore_delete(TX_SEMAPHORE *semaphore_ptr)
{
TX_INTERRUPT_SAVE_AREA
TX_THREAD *thread_ptr; /* Working thread pointer */
/* Disable interrupts to remove the semaphore from the created list. */
TX_DISABLE
/* Decrement the number of semaphores created. */
_tx_semaphore_created_count--;
/* Clear the semaphore ID to make it invalid. */
semaphore_ptr -> tx_semaphore_id = 0;
#def 从_tx_semaphore_created_ptr 队列移除信号量
/* See if the semaphore is the only one on the list. */
if (semaphore_ptr == semaphore_ptr -> tx_semaphore_created_next)
{
/* Only created semaphore, just set the created list to NULL. */
_tx_semaphore_created_ptr = TX_NULL;
}
else
{
/* Link-up the neighbors. */
(semaphore_ptr -> tx_semaphore_created_next) -> tx_semaphore_created_previous =
semaphore_ptr -> tx_semaphore_created_previous;
(semaphore_ptr -> tx_semaphore_created_previous) -> tx_semaphore_created_next =
semaphore_ptr -> tx_semaphore_created_next;
/* See if we have to update the created list head pointer. */
if (_tx_semaphore_created_ptr == semaphore_ptr)
/* Yes, move the head pointer to the next link. */
_tx_semaphore_created_ptr = semaphore_ptr -> tx_semaphore_created_next;
}
/* Temporarily disable preemption. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Walk through the semaphore list to resume any and all threads suspended
on this semaphore. */
#def 删除tx_semaphore_suspension_list中所有线程,相应数据结构信息,并恢复线程
thread_ptr = semaphore_ptr -> tx_semaphore_suspension_list;
while (semaphore_ptr -> tx_semaphore_suspended_count)
{
/* Lockout interrupts. */
TX_DISABLE
/* Clear the cleanup pointer, this prevents the timeout from doing
anything. */
thread_ptr -> tx_suspend_cleanup = TX_NULL;
/* Temporarily disable preemption again. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
#def 去激活定时器
/* Yes, deactivate the thread's timer just in case. */
_tx_timer_deactivate(&(thread_ptr -> tx_thread_timer));
/* Set the return status in the thread to TX_DELETED. */
thread_ptr -> tx_suspend_status = TX_DELETED;
/* Move the thread pointer ahead. */
thread_ptr = thread_ptr -> tx_suspended_next;
#def 恢复线程
/* Resume the thread. */
_tx_thread_resume(thread_ptr -> tx_suspended_previous);
/* Decrease the suspended count. */
semaphore_ptr -> tx_semaphore_suspended_count--;
}
/* Disable interrupts. */
TX_DISABLE
/* Release previous preempt disable. */
_tx_thread_preempt_disable--;
/* Restore interrupts. */
TX_RESTORE
#def 开中断(可以抢占了),有可能更高级线程需要执行,例如_tx_thread_resume会恢复高优先级线程,这里进行调度
/* Check for preemption. */
if (_tx_thread_current_ptr != _tx_thread_execute_ptr)
/* Transfer control to system. */
_tx_thread_system_return();
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}