uCOS任务信号量相关函数代码理解

强调任务信号量思想:① 任务信号量只是一个标志,获取成功就是指把信号量计数值减1;释放就是指把信号量计数值加1(溢出则计数值不变)。② 获取信号量需要判断信号量是否可用(大于0),如果可用,表明一定会获取成功(大于0的数可以进行减1还是>=0),那么就可以获取当前时间戳与之前最新一次释放时间戳作差,作为此次等待信号量的时间(保存在TCB中的元素SemPendTime中)。③ 在释放函数中,因为释放不用考虑任务信号量是否可用,释放就是增加信号量可用数的操作,所以如果在一系列安全、参数检查检查正常后,就表示可以释放了,此时就可以获取当前时间戳作为此次释放任务信号量的时间戳(保存在TCB中的TS元素中)。

TCB中与任务信号量相关的元素

{

CPU_TS  SemPendTime; 任务等待自身任务信号量的等待时间。

CPU_TS  SemPendTimeMax; 任务多次等待任务信号量的最久一次的等待时间。

OS_SEM_CTR  SemCtr; 任务信号量计数值。

CPU_TS  TS; 任务信号量发布/释放时的时间戳。

CPU_INT08U  SemID; 第三方调试器和跟踪程序的唯一ID。

}

OSTaskSemPend:任务信号量获取函数

{

函数思想:任务信号量获取,是获取任务自身的信号量。如果自身信号量个数大于0(可用),就获取成功;否则,需要判断是否阻塞,从而判断是否等待任务信号量。

4个入口参数:超时时间;选项;返回的时间戳(任务信号量最新释放时的时间戳);返回的错误类型。

返回:任务信号量计数值

函数过程:① 首先进行安全、参数、选项等检查,不允许在中断中被调用;选项包括OS_OPT_PEND_BLOCKING和OS_OPT_PEND_NON_BLOCKING。② 如果任务自身信号量计数值(OSTCBCurPtr->SemCtr)大于0,表明任务信号量可用,可以获取信号量,任务信号量个数减1;获取任务信号量最新一次释放时的时间戳(OSTCBCurPtr->TS),和当前时间戳作差,就是最近一次等待任务信号量的时间;并与OSTCBCurPtr->SemPendTimeMax做比较,更新SemPendTimeMax的值。③ 如果任务信号量不可用,需要进入阻塞以等待信号量释放。如果用户选择OS_OPT_PEND_NON_BLOCKING,不等待阻塞,那么就返回需要等待阻塞的错误类型,把那个返回信号量计数值为0。④ 如果信号量不可用,但选择了进入阻塞。那么首先判断调度器是否被锁,因为调度器被锁,任务得不到调度,自然不能改变任务的状态。如果没有锁调度器,那么调用OS_CRITICAL_ENTER_CPU_EXIT()锁住调度器,因为接下来是对该任务进行阻塞操作,操作的是TCB,是全局变量,作为临界段资源,需要被保护。调用OS_Pend()对该任务进行阻塞。注意:这里任务信号量并不是内核对象,所以函数入口参数为(OS_PEND_DATA *)0和(OS_PEND_OBJ  *)0。因为任务信号量没有等待列表,所以阻塞仅仅是调用OS_TaskBlock()将任务移出就绪列表和插入时基列表(如果超时时间不为0)。然后调用OSSched()进行任务切换。⑤ 进行到这一步,意味着超时结束、被中止,或者有任务释放了该任务信号量,任务信号量可用,任务等到了该任务信号量。这么多种情况都是跟任务状态有关,所以根据任务状态switch-case。如果是OS_STATUS_PEND_OK表示正常等到了任务信号量,进行步骤②的操作,更新TCB中等待任务信号量的与时间相关的元素SemPendTime和SemPendTimeMax。如果是OS_STATUS_PEND_ABORT,表示等待被中止,返回任务信号量最近一次释放时的时间戳和相应的错误类型。如果是在超时时间中没等到信号量OS_STATUS_PEND_TIMEOUT,也是返回任务信号量最近一次释放时的时间戳和相应的错误类型。⑥ 最后返回任务信号量当前计数值。

}

OSTaskSemPost:任务信号量释放函数

{

虽然任务只能等待任务自身的任务信号量,但是其他所有的任务或者中断都可以向该任务释放信号量。

参数不同:任务信号量释放函数的入口参数个数同内核信号量的一样,也是3个。只不过任务信号量针对任务,所以第一个参数是指向目标任务的TCB指针,而内核信号量时指向创建的信号量的指针。

返回:返回任务信号量个数。如果从ISR调用,则为0;如果溢出,也返回0;另外参数非法之类的也返回0。所以如果当真在实现功能时,进行了复杂操作,出现返回0的情况;或者操作不当返回0,需要通过返回的错误类型(p_err)判断是哪种情况。

过程:除参数有些不同,过程与内核信号量都差不多。① 首先获取当前时间戳作为最新一次释放任务信号量的时间戳,该时间戳会在后面p_tcb->TS = ts; 赋值给任务TCB中用来记录释放任务信号量时间戳元素TS。(因为任务信号量只是一个计数标志,只要调用任务信号量释放函数,在一系列安全、参数检查都正常的情况,该任务信号量必然被释放,任务信号量计数值要么正常加1,要么就不变,因为加1就溢出了。所以在检查正常之后即是获取当前时间戳的代码,并把它作为该任务信号量最新一次释放的时间戳)。② 如果使能了中断延迟发布,且在中断中调用该函数,则调用OS_IntQPost()将任务信号量发布到中断消息队列。③ 如果是在任务中发送,调用函数OS_TaskSemPost()。

--调用OS_TaskSemPost()

{

过程:因为任务信号量是针对的任务,每个任务中只定义了一个32位变量对任务信号量进行计数,并不存在内核信号量结构体中的等待列表。任务阻塞并不进入等待列表。我们释放任务信号量,是通过任务信号量指向的目标任务的状态判断任务是否在等待该任务信号量。释放任务信号量后,任务信号量可用,其他操作都相似:如果任务在阻塞,就将任务解除阻塞,任务得到该信号量。如果没有任务在等待,就将任务信号量计数值加1。

① 如果参数目标TCB指针为空,就让目标TCB指向当前TCB,释放当前任务的任务信号量。② 如果任务状态没有等待态(pend),说明目标任务暂时不需要获取任务信号量,或者可用任务信号量足够,目标任务已经获取了,无须等待;没有等待该任务信号量的任务,那么就通过sizeof(OS_SEM_CTR),判断任务信号量是否会溢出,溢出就返回错误类型“OS_ERR_SEM_OVF”,并返回任务信号量计数值为0;否则任务信号量计数值加1。③ 如果任务状态有等待状态,并且任务是在等待任务信号量,调用OS_Post()将该任务信号量发送给等待的任务,该函数会把任务插入就绪列表,既然就绪列表中任务个数有改变,那么紧接着就调用判断是否opt选择了OS_OPT_POST_NO_SCHED,从而判断是否调用OSSched()。④ 如果任务处于等待状态,但是不是等待任务信号量,那么进行步骤②的操作。⑤ 最后返回当前任务信号量计数值。

}

}

猜你喜欢

转载自blog.csdn.net/m0_43443861/article/details/126373440
今日推荐