rt-thread与ucos中任务退出时如何调用退出函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Longyu_wlz/article/details/82987316

rt-thread与ucos中任务退出时如何调用退出函数

rt-thread与ucos中都提供了删除任务的函数,同时系统也支持任务正常终止。相较于死循环的任务执行函数,这是一大改进。

任务的创建很容易,也不易产生问题。任务的销毁却十分不易,任务占有资源的释放更是令人头疼。以前我一直调用系统提供的删除任务的函数强制删除任务,这在大多数时间并没有造成太大的问题,但这样的方式其实不太合理。

任务删除时必须考虑当前任务的状态,当前任务占有的ipc资源,以及当前任务在系统的哪个表,这都是删除一个任务必须考虑的情况。如果待删除任务在等待链表中,那么删除该任务意味着从等待表中移除该任务节点。如果待删除任务在就绪表中,那么删除该任务需要从就绪表中移除,这样此任务就不再会被系统调度运行。

删除一个占用ipc资源的任务需要特别小心。例如当任务未释放占有的mutex时,强制删除任务将造成其它在就绪表中永久等待mutex的任务永久阻塞,这常常是我们不想看到的,这也是一个常见的问题。

在rt-thread的内核源码thread.c中有rt_thread_exit函数,此函数在任务正常终止后被调用,这是我最初在调试时发现的。但我一直不明白为什么在任务正常终止后系统就能调用这个函数,有一天突然发现这与任务栈中预存的数据有关。

rt-thread的任务初始化函数中调用了rt_hw_stack_init子函数,调用的具体语句如下:

  thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (void *)((char *)thread->stack_addr + thread->stack_size - 4),
                                          (void *)rt_thread_exit);

在上面的函数调用中,将rt_thread_exit函数地址作为参数传入到rt_hw_stack_init函数中,rt_hw_stack_init函数的部分源码如下:

 rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
	rt_uint8_t *stack_addr, void *texit)
{
	rt_uint32_t *stk;

    stack_addr += sizeof(rt_uint32_t);
    stack_addr  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8);
    stk      = (rt_uint32_t *)stack_addr;

    *(--stk) = (rt_uint32_t)tentry;         /* entry point */
    *(--stk) = (rt_uint32_t)texit;          /* lr */
    ...................................................................

上面的最后一行代码将传入的rt_thread_exit函数地址压入到任务栈中,旁边的注释写着lrlr是链接寄存器,在任务栈初始化时就保存了rt_thread_exit函数的地址,这就是谜底。

任务的执行函数使用任务栈,任务栈动态增减。当执行函数返回,任务栈弹栈,作为lr的rt_thread_exit的地址恢复到PC中,程序继续执行此函数,完成任务的正常退出。神奇吗?这就是任务退出的魔法。

ucos-III中也能够找到类似的实现,不再赘述。

猜你喜欢

转载自blog.csdn.net/Longyu_wlz/article/details/82987316