μC/OS-III---I笔记5---多值信号量
Posted Little_Village
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了μC/OS-III---I笔记5---多值信号量相关的知识,希望对你有一定的参考价值。
多值信号量
操作系统中利用信号量解决进程间的同步和互斥(互斥信号量)的问题,在多道程序环境下,操作系统如何实现进程之间的同步和互斥显得极为重要。比如对同一部分资源的访问是要互斥,不能在另一个进程A在访问的时候被其他的进程再访问这样两个进程相互影响就无法保证正常的访问操作。另一方面是任务间的同步,比如一个任务A依赖于另一个任务B的处理结果,在B处理完成时给A一个就绪信号让A开始使用结果进行相关处理。
同样在使用之前要先定义一个信号量,然后调用创建OSSemCreate()函数创建信号量注意到函数内的等待队列初始化。每个信号量都有一个等待队列,当一个任务使用完公共资源部分或者完成了同步处理的一部分就会发布一个信号给等待的任务,等待任务会接到这个信号从而进行相应的操作。等待的任务由OS_PEND_LIST这个结构体变量管理TailPtr指向等待队列的最后一个Head指向第一个。
如图的数据结构:
1.创建多值信号量,这实际上是对多值信号量的一个初始化的部分这在USOC内多处都是这样叫的。
#if OS_CFG_SEM_EN > 0u
/*
************************************************************************************************************************
* CREATE A SEMAPHORE
*
* Description: This function creates a semaphore.
*
* Arguments : p_sem is a pointer to the semaphore to initialize. Your application is responsible for
* allocating storage for the semaphore.
*
* p_name is a pointer to the name you would like to give the semaphore.
*
* cnt is the initial value for the semaphore.
* If used to share resources, you should initialize to the number of resources available.
* If used to signal the occurrence of event(s) then you should initialize to 0.
*
* p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_NONE if the call was successful
* OS_ERR_CREATE_ISR if you called this function from an ISR
* OS_ERR_ILLEGAL_CREATE_RUN_TIME if you are trying to create the semaphore after you
* called OSSafetyCriticalStart().
* OS_ERR_NAME if \'p_name\' is a NULL pointer
* OS_ERR_OBJ_CREATED if the semaphore has already been created
* OS_ERR_OBJ_PTR_NULL if \'p_sem\' is a NULL pointer
* OS_ERR_OBJ_TYPE if \'p_sem\' has already been initialized to a different
* object type
*
* Returns : none
************************************************************************************************************************
*/
void OSSemCreate (OS_SEM *p_sem,
CPU_CHAR *p_name,
OS_SEM_CTR cnt,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* Not allowed to be called from an ISR */
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_sem == (OS_SEM *)0) { /* Validate \'p_sem\' */
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
OS_CRITICAL_ENTER();
//信号量变量初始化
p_sem->Type = OS_OBJ_TYPE_SEM; /* Mark the data structure as a semaphore */
p_sem->Ctr = cnt; /* Set semaphore value */
p_sem->TS = (CPU_TS)0;
p_sem->NamePtr = p_name; /* Save the name of the semaphore */
//信号量上的等待队列初始化
OS_PendListInit(&p_sem->PendList); /* Initialize the waiting list */
#if OS_CFG_DBG_EN > 0u
OS_SemDbgListAdd(p_sem);
#endif
//信号量计数加1
OSSemQty++;
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
}
2.创建完信号量,然后请求信号量,对于多值信号量先检查信号量是否可用,可用就直接返回信号量的可用数量,如果不可用选择阻塞任务,或者直接继续运行任务。
等待信号量流程:
内部调用的函数的中文注释源码:
************************************************************************************************************************
* PEND ON SEMAPHORE
*
* Description: This function waits for a semaphore.
*
* Arguments : p_sem is a pointer to the semaphore
*
* timeout is an optional timeout period (in clock ticks). If non-zero, your task will wait for the
* resource up to the amount of time (in \'ticks\') specified by this argument. If you specify
* 0, however, your task will wait forever at the specified semaphore or, until the resource
* becomes available (or the event occurs).
*
* opt determines whether the user wants to block if the semaphore is not available or not:
*
* OS_OPT_PEND_BLOCKING
* OS_OPT_PEND_NON_BLOCKING
*
* p_ts is a pointer to a variable that will receive the timestamp of when the semaphore was posted
* or pend aborted or the semaphore deleted. If you pass a NULL pointer (i.e. (CPU_TS*)0)
* then you will not get the timestamp. In other words, passing a NULL pointer is valid
* and indicates that you don\'t need the timestamp.
*
* p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_NONE The call was successful and your task owns the resource
* or, the event you are waiting for occurred.
* OS_ERR_OBJ_DEL If \'p_sem\' was deleted
* OS_ERR_OBJ_PTR_NULL If \'p_sem\' is a NULL pointer.
* OS_ERR_OBJ_TYPE If \'p_sem\' is not pointing at a semaphore
* OS_ERR_OPT_INVALID If you specified an invalid value for \'opt\'
* OS_ERR_PEND_ABORT If the pend was aborted by another task
* OS_ERR_PEND_ISR If you called this function from an ISR and the result
* would lead to a suspension.
* OS_ERR_PEND_WOULD_BLOCK If you specified non-blocking but the semaphore was not
* available.
* OS_ERR_SCHED_LOCKED If you called this function when the scheduler is locked
* OS_ERR_STATUS_INVALID Pend status is invalid
* OS_ERR_TIMEOUT The semaphore was not received within the specified
* timeout.
*
*
* Returns : The current value of the semaphore counter or 0 if not available.
************************************************************************************************************************
*/
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
OS_PEND_DATA pend_data;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_PEND_ISR;
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_sem == (OS_SEM *)0) { /* Validate \'p_sem\' */
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_SEM_CTR)0);
}
switch (opt) { /* Validate \'opt\' */
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_sem->Type != OS_OBJ_TYPE_SEM) { /* Make sure semaphore was created */
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_SEM_CTR)0);
}
#endif
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS)0; /* Initialize the returned timestamp */
}
CPU_CRITICAL_ENTER();
//资源有效直接返回可用的数量
if (p_sem->Ctr > (OS_SEM_CTR)0) { /* Resource available? */
p_sem->Ctr--; /* Yes, caller may proceed */
if (p_ts != (CPU_TS *)0) {
*p_ts = p_sem->TS; /* get timestamp of last post */
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return (ctr);
}
//是否阻塞任务
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */
ctr = p_sem->Ctr; /* No */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return (ctr);
} else { /* Yes */
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can\'t pend when the scheduler is locked */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_SEM_CTR)0);
}
}
/* Lock the scheduler/re-enable interrupts */
OS_CRITICAL_ENTER_CPU_EXIT();
//阻塞任务进行等待
OS_Pend(&pend_data, /* Block task pending on Semaphore */
(OS_PEND_OBJ *)((void *)p_sem),
OS_TASK_PEND_ON_SEM,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
//挂起当前任务执行其他任务
OSSched(); /* Find the next highest priority task ready to run */
//当前任务重新就绪检查返回等待结果
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: /* We got the semaphore */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: /* Indicate that we aborted */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn\'t get semaphore within timeout */
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
*p_err = OS_ERR_TIMEOUT;
break;
case OS_STATUS_PEND_DEL: /* Indicate that object pended on has been deleted */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_OBJ_DEL;
break;
default:
*p_err = OS_ERR_STATUS_INVALID;
CPU_CRITICAL_EXIT();
return ((OS_SEM_CTR)0);
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT();
return (ctr);
}
************************************************************************************************************************
* BLOCK A TASK PENDING ON EVENT
*
* Description: This function is called to place a task in the blocked state waiting for an event to occur. This function
* exist because it is common to a number of OSxxxPend() services.
*
* Arguments : p_pend_data is a pointer to an object used to link the task being blocked to the list of task(s)
* ----------- pending on the desired object.
* p_obj is a pointer to the object to pend on. If there are no object used to pend on then
* ----- the caller must pass a NULL pointer.
*
* pending_on Specifies what the task will be pending on:
*
* OS_TASK_PEND_ON_FLAG
* OS_TASK_PEND_ON_TASK_Q <- No object (pending for a message sent to the task)
* OS_TASK_PEND_ON_MUTEX
* OS_TASK_PEND_ON_Q
* OS_TASK_PEND_ON_SEM
* OS_TASK_PEND_ON_TASK_SEM <- No object (pending on a signal sent to the task)
*
* timeout Is the amount of time the task will wait for the event to occur.
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
void OS_Pend (OS_PEND_DATA *p_pend_data,
OS_PEND_OBJ *p_obj,
OS_STATE pending_on,
OS_TICK timeout)
{
OS_PEND_LIST *p_pend_list;
OSTCBCurPtr->PendOn = pending_on; /* Resource not available, wait until it is */
OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
//×èÈûÈÎÎñ
OS_TaskBlock(OSTCBCurPtr, /* Block the task and add it to the tick list if needed */
timeout);
if (p_obj != (OS_PEND_OBJ *)0) { /* Add the current task to the pend list ... */
//È¡³öÐźÅÁ¿Ïµĵȴýlist
p_pend_list = &p_obj->PendList; /* ... if there is an object to pend on */
//ÉèÖÃdataµÄµÈ´ý¶ÔÏó
p_pend_data->PendObjPtr = p_obj; /* Save the pointer to the object pending on */
//p_pend_data³õʼ»¯
OS_PendDataInit((OS_TCB *)OSTCBCurPtr, /* Initialize the remaining field */
(OS_PEND_DATA *)p_pend_data,
(OS_OBJ_QTY )1);
//°´ÈÎÎñÓÅÏȼ¶ÅÅÐò½«µÈ´ýÊý¾Ý²åÈëµÈ´ý¶ÓÁÐ
OS_PendListInsertPrio(p_pend_list, /* Insert in the pend list in priority order */
p_pend_data);
} else {
OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY )0; /* If no object being pended on the clear these fields */
OSTCBCurPtr->PendDataTblPtr = (OS_PEND_DATA *)0; /* ... in the TCB */
}
#if OS_CFG_DBG_EN > 0u
OS_PendDbgNameAdd(p_obj,
OSTCBCurPtr);
#endif
}
************************************************************************************************************************
* BLOCK A TASK
*
* Description: This function is called to remove a task from the ready list and also insert it in the timer tick list if
* the specified timeout is non-zero.
*
* Arguments : p_tcb is a pointer to the OS_TCB of the task block
* -----
*
* timeout is the desired timeout
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
void OS_TaskBlock (OS_TCB *p_tcb,
OS_TICK timeout)
{
OS_ERR err;
//检查超时时间为0?
if (timeout > (OS_TICK)0) { /* Add task to tick list if timeout non zero */
//按超时时间设置延时阻塞,具体见时间管理
OS_TickListInsert(p_tcb,
timeout,
OS_OPT_TIME_TIMEOUT,
&err);
//检查错误码
if (err == OS_ERR_NONE) {
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
} else {
p_tcb->TaskState = OS_TASK_STATE_PEND;
}
} else {
//是 永久等待
p_tcb->TaskState = OS_TASK_STATE_PEND;
}
OS_RdyListRemove(p_tcb);
}
************************************************************************************************************************
* INITIALIZE A WAIT LIST TABLE
*
* Description: This function is called to initialize the fields of a table of OS_PEND_DATA entries. It\'s assumed that
* the .PendObjPtr field of each entry in the table is set by the caller and thus will NOT be touched by
* this function.
*
* Arguments : p_tcb is a pointer to the TCB of the task that we want to pend abort.
* -----
*
* p_pend_data_tbl is a pointer to a table (see below) of OS_PEND_DATA elements to initialize.
* ---------------
*
* .PendObjPtr .RdyObjPtr .RdyMsgPtr .RdyMsgSize .RdyTS .TCBPtr .NextPtr .PrevPtr
* +-----------+----------+----------+-----------+------+-------+--------+--------+ ^
* p_pend_data_tbl-> | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ |
* | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ |
* | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ size
* | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ |
* | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ |
* | ? | 0 | 0 | 0 | 0 | p_tcb | 0 | 0 | |
* +-----------+----------+----------+-----------+------+-------+--------+--------+ V
*
* tbl_size is the size of the table in number of entries
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application must not call it.
*
* 2) It\'s possible for the table to be of size 1 when multi-pend is not used
*
* 3) Note that the .PendObjPtr is NOT touched because it\'s assumed to be set by the caller.
************************************************************************************************************************
*/
void OS_PendDataInit (OS_TCB *p_tcb,
OS_PEND_DATA *p_pend_data_tbl,
OS_OBJ_QTY tbl_size)
{
OS_OBJ_QTY i;
//将任务控制块对应的指针参数初始化
p_tcb->PendDataTblEntries = tbl_size; /* Link the TCB to the beginning of the table */
p_tcb->PendDataTblPtr = p_pend_data_tbl;
//这里一般调用都是执行一次
for (i = 0u; i < tbl_size; i++) {
p_pend_data_tbl->NextPtr = (OS_PEND_DATA *)0; /* Initialize all the fields */
p_pend_data_tbl->PrevPtr = (OS_PEND_DATA *)0;
p_pend_data_tbl->RdyObjPtr = (OS_PEND_OBJ *)0;
p_pend_data_tbl->RdyMsgPtr = (void *)0;
p_pend_data_tbl->RdyMsgSize = (OS_MSG_SIZE )0;
p_pend_data_tbl->RdyTS = (CPU_TS )0;
p_pend_data_tbl->TCBPtr = p_tcb; /* Every entry points back to the TCB of the task */
p_pend_data_tbl++;
}
}
************************************************************************************************************************
* INSERT PEND DATA BASED ON IT\'S PRIORITY IN A LIST
*
* Description: This function is called to place an OS_PEND_DATA entry in a linked list based on its priority. The
* highest priority being placed at the head of the list. It\'s assumed that the OS_PEND_DATA entry to
* insert points to the TCB of the task being inserted. The TCB is also assumed to contain the priority
* of the task in its .Prio field.
*
* CASE 0: Insert in an empty list.
*
* OS_PEND_LIST
* +---------------+
* | TailPtr |-> 0
* +---------------+
* | HeadPtr |-> 0
* +---------------+
* | NbrEntries=0 |
* +---------------+
*
*
*
* CASE 1: Insert BEFORE or AFTER an OS_TCB
*
* OS_PEND_LIST
* +--------------+ OS_PEND_DATA
* | TailPtr |--+---> +------------+
* +--------------+ | | NextPtr |->0
* | HeadPtr |--/ +------------+
* +--------------+ 0<-| PrevPtr |
* | NbrEntries=1 | +------------+
* +--------------+ | |
* +------------+
* | |
* +------------+
*
*
* OS_PEND_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_PEND_DATA OS_PEND_DATA | OS_PEND_DATA
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* | | | | | |
* +------------+ +------------+ +------------+
* | | | | | |
* +------------+ +------------+ +------------+
*
*
* Arguments : p_pend_list is a pointer to the OS_PEND_LIST where the OS_PEND_DATA entry will be inserted
* -----------
*
* p_pend_data is the OS_PEND_DATA to insert in the list
* -----------
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
*
* 2) \'p_pend_data->TCBPtr->Prio\' contains the priority of the TCB associated with the entry to insert.
* We can compare this priority with the priority of other entries in the list.
************************************************************************************************************************
*/
void OS_PendListInsertPrio (OS_PEND_LIST *p_pend_list,
OS_PEND_DATA *p_pend_data)
{
OS_PRIO prio;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
OS_PEND_DATA *p_pend_data_prev;
OS_PEND_DATA *p_pend_data_next;
p_tcb = p_pend_data->TCBPtr; /* Obtain the priority of the task to insert */
prio = p_tcb->Prio;
if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { /* CASE 0: Insert when there are no entries */
p_pend_list->NbrEntries = (OS_OBJ_QTY)1; /* This is the first entry */
p_pend_data->NextPtr = (OS_PEND_DATA *)0; /* No other OS_PEND_DATAs in the list */
p_pend_data->PrevPtr = (OS_PEND_DATA *)0;
p_pend_list->HeadPtr = p_pend_data; /* */
p_pend_list->TailPtr = p_pend_data;
} else {
p_pend_list->NbrEntries++; /* CASE 1: One more OS_PEND_DATA in the list */
p_pend_data_next = p_pend_list->HeadPtr;
while (p_pend_data_next != (OS_PEND_DATA *)0) { /* Find the position where to insert */
p_tcb_next = p_pend_data_next->TCBPtr;
if (prio < p_tcb_next->Prio) {
break; /* Found! ... insert BEFORE current */
} else {
p_pend_data_next = p_pend_data_next->NextPtr; /* Not Found, follow the list */
}
}
//开始插入
//末尾
if (p_pend_data_next == (OS_PEND_DATA *)0) { /* TCB to insert is lower in prio */
p_pend_data->NextPtr = (OS_PEND_DATA *)0; /* ... insert at the tail. */
p_pend_dat以上是关于μC/OS-III---I笔记5---多值信号量的主要内容,如果未能解决你的问题,请参考以下文章