延时操作
延时操作是操作系统中经常遇到的一种情形。延时的原因很多,有的时候是为了等待外设芯片处理结束,有的时候是为了暂时释放cpu的使用权,有的就是为了希望在一段时间获取资源,如果没法在单位时间内获取,放弃等待。但是不管怎么说,延时都是操作系统必不可少的一个工作。下面我们就看看延时是怎么实现的,
static void tick_list_priority_insert(LIST *head, RAW_TASK_OBJ *task_ptr) { RAW_U32 val; LIST *q,*start, *end; RAW_TASK_OBJ *task_iter_temp; start = end = head; val = task_ptr->tick_remain; for (q = start->next; q != end; q = q->next) { task_iter_temp = list_entry(q, RAW_TASK_OBJ, tick_list); /*sorted by remain time*/ if ((task_iter_temp->tick_match - raw_tick_count) > val) { break; } } list_insert(q, &task_ptr->tick_list); } void tick_list_insert(RAW_TASK_OBJ *task_ptr, RAW_U32 time) { LIST *tick_head_ptr; RAW_U16 spoke; if (time) { task_ptr->tick_match = raw_tick_count + time; task_ptr->tick_remain = time; spoke = (RAW_U16)(task_ptr->tick_match & (TICK_HEAD_ARRAY - 1) ); tick_head_ptr = &tick_head[spoke]; tick_list_priority_insert(tick_head_ptr, task_ptr); task_ptr->tick_head = tick_head_ptr; } }
延时的代码其实不是很多,所以我在这里把最重要的两个函数给粘贴到这里了。因为每个线程都有可能延时,那么怎么处理这些线程之间的关系就是我们需要做的事情了。我们看到了,我们直接用tick_match表示线程需要等待的那个时间点就可以了。当然,tick是不断增加的,我们可以把尾数相同的线程按照高低顺序排列在一起,这样在对应的tick到来的时候,就直接按照尾数查找就可以了,tick_list_priority_insert就是干了这么一件事情。
那么,tick什么时候到期呢?到期又该怎么处理呢,我们接着往下看,
void tick_list_update(void) { LIST *tick_head_ptr; RAW_TASK_OBJ *p_tcb; LIST *iter; LIST *iter_temp; RAW_U16 spoke; RAW_SR_ALLOC(); RAW_CRITICAL_ENTER(); raw_tick_count++; spoke = (RAW_U16)(raw_tick_count & (TICK_HEAD_ARRAY - 1) ); tick_head_ptr = &tick_head[spoke]; iter = tick_head_ptr->next; while (RAW_TRUE) { /*search all the time list if possible*/ if (iter != tick_head_ptr) { iter_temp = iter->next; p_tcb = list_entry(iter, RAW_TASK_OBJ, tick_list); /*Since time list is sorted by remain time, so just campare the absolute time*/ if (raw_tick_count == p_tcb->tick_match) { switch (p_tcb->task_state) { case RAW_DLY: p_tcb->block_status = RAW_B_OK; p_tcb->task_state = RAW_RDY; tick_list_remove(p_tcb); add_ready_list(&raw_ready_queue, p_tcb); break; case RAW_PEND_TIMEOUT: p_tcb->block_status = RAW_B_TIMEOUT; p_tcb->task_state = RAW_RDY; p_tcb->block_obj = 0; tick_list_remove(p_tcb); /*remove task on the block list because task is timeout*/ list_delete(&p_tcb->task_list); add_ready_list(&raw_ready_queue, p_tcb); break; case RAW_PEND_TIMEOUT_SUSPENDED: p_tcb->block_status = RAW_B_TIMEOUT; p_tcb->task_state = RAW_SUSPENDED; p_tcb->block_obj = 0; tick_list_remove(p_tcb); /*remove task on the block list because task is timeout*/ list_delete(&p_tcb->task_list); break; case RAW_DLY_SUSPENDED: p_tcb->task_state = RAW_SUSPENDED; p_tcb->block_status = RAW_B_OK; tick_list_remove(p_tcb); break; default: #if (CONFIG_RAW_ASSERT > 0) RAW_ASSERT(0); #endif break; } iter = iter_temp; } /*if current task time out absolute time is not equal current system time, just break because timer list is sorted*/ else { break; } } /*finish all the time list search */ else { break; } } RAW_CRITICAL_EXIT(); }
这个函数是在时钟中断的时候被调用的,根据函数的先后顺序,看看函数实现了哪些功能,
(1)自增raw_tick_count;
(2)根据尾数获取tick队列的头指针;
(3)开始循环迭代处理延时线程;
a)如果没有没有延时线程,循环跳出;
b)如果线程的终点tick和当前tick不匹配,跳出循环,因为tick都是排序好的,所以后面的tick肯定不满足要求;
c)如果当前tick满足要求,根据线程状态进行处理,主要分为延时、阻塞超时、延时挂起、阻塞超时挂起四种状态;
d)获取下一个延时线程,观察是否满足要求,如果是继续回到c,否则退出循环。
(4)函数返回,继续时钟中断的剩余操作。
最后,我们补充一下关于有限时间等待的知识。就像以前关于互斥操作的内容一样,其实某些情况下,我们是有时间限制的。一段时间没有获取资源,我们就不希望等待了,所以这里的延时操作还包括这部分的内容,我们看看阻塞函数的相关代码就明白了。
RAW_U16 raw_pend_object(RAW_COMMON_BLOCK_OBJECT *block_common_obj, RAW_TASK_OBJ *task_ptr, RAW_U32 timeout) { #if (CONFIG_RAW_ASSERT > 0) if (timeout == 0) { RAW_ASSERT(0); } #endif task_ptr->block_obj = block_common_obj; if (timeout == RAW_WAIT_FOREVER) { task_ptr->task_state = RAW_PEND; } /*task is blocked with timeout*/ else { tick_list_insert(task_ptr,timeout); task_ptr->task_state = RAW_PEND_TIMEOUT; } /*Remove from the ready list*/ remove_ready_list(&raw_ready_queue, task_ptr); if (block_common_obj->block_way == RAW_BLOCKED_WAY_FIFO) { list_insert(&block_common_obj->block_list, &task_ptr->task_list); } else { /*add to the priority sorted block list*/ add_to_priority_list(&block_common_obj->block_list, task_ptr); } return RAW_SUCCESS; }
大家留意一下这里timeout参数的处理过程,关注一下对应的tick_list_insert函数,这样就可以明白我的意思了。
实时系统中的定时器
关于定时器的内容,其实我们之前也讨论过,也书写过相应的代码,但是表达得比较晦涩,效率也比较低。所以我们这里重新再讲一下定时器的相关代码,看看嵌入式系统中的定时器是怎么实现的。在我们之前讨论线程延时的时候就使用hash的方法,将不同的线程归类到不同的延时队列当中,并且按照时间长短先后排列,这样在最短的时间内就可以寻找到最合适的线程了。本质上,线程延时和定时器的基本原理是一样的。唯一的区别就是,线程延时响应的优先级要高一些,而定时器一般由独立线程完成,rawos也是这么做的。
void timer_task(void *pa) { RAW_U16 position; LIST *timer_head_ptr; LIST *iter; LIST *iter_temp; RAW_TIMER *timer_ptr; timer_sem.count = 0; while (1) { /*timer task will be blocked after call this function*/ raw_semaphore_get(&timer_sem, RAW_WAIT_FOREVER); /*Disable the system schedule we do not need disable interrupt since nothing to do with interrupt*/ raw_disable_sche(); /*calculate which timer_head*/ raw_timer_count++; position = (RAW_U16)(raw_timer_count & (TIMER_HEAD_NUMBERS - 1) ); timer_head_ptr = &timer_head[position]; iter =timer_head_ptr->next; while (RAW_TRUE) { /*if timer exits*/ if (iter !=timer_head_ptr) { /*Must use iter_temp because iter may be remove later.*/ iter_temp = iter->next; timer_ptr = list_entry(iter, RAW_TIMER, timer_list); /*if timeout*/ if (raw_timer_count == timer_ptr->match) { /*remove form timer list*/ timer_list_remove(timer_ptr); /*if timer is reschedulable*/ if (timer_ptr->reschedule_ticks) { /*Sort by remain time*/ timer_ptr->remain = timer_ptr->reschedule_ticks; timer_ptr->match = raw_timer_count + timer_ptr->remain; position = (RAW_U16)(timer_ptr->match & (TIMER_HEAD_NUMBERS - 1)); timer_ptr->to_head = &timer_head[position]; timer_list_priority_insert(&timer_head[position], timer_ptr); } /*Any way both condition need to call registered timer function*/ if (timer_ptr->raw_timeout_function) { timer_ptr->raw_timeout_function(timer_ptr->raw_timeout_param); } iter = iter_temp; } else { break; } } /*exit because timer is not exit*/ else { break; } } raw_enable_sche(); } }
由于基本原理和之前的线程延时是一样的,所以这里就不重复了。定时器的基本操作其实也不多,主要包括定时器创建、启动定时器、修改定时器、关闭定时器、删除定时器共五个基本函数,大家可以和我一起慢慢看过来。
RAW_U16 raw_timer_create(RAW_TIMER *timer_ptr, RAW_U8 *name_ptr, RAW_VOID (*expiration_function)(RAW_U32), RAW_U32 expiration_input, RAW_U32 initial_ticks, RAW_U32 reschedule_ticks, RAW_U8 auto_activate) { #if (RAW_TIMER_FUNCTION_CHECK > 0) if (timer_ptr == 0) { return RAW_NULL_OBJECT; } if (expiration_function == 0) { return RAW_NULL_POINTER; } #endif timer_ptr->name = name_ptr; timer_ptr->raw_timeout_function = expiration_function; timer_ptr->raw_timeout_param = expiration_input; timer_ptr->init_count = initial_ticks; timer_ptr->reschedule_ticks = reschedule_ticks; timer_ptr->remain = 0; timer_ptr->match = 0; timer_ptr->timer_state = TIMER_DEACTIVE; timer_ptr->to_head = 0; list_init(&timer_ptr->timer_list); if (auto_activate) { raw_timer_activate(timer_ptr); } return RAW_SUCCESS; }
创建定时器的操作很简单,主要是把输入的参数存入到RAW_TIMER结构里面。相关的参数包括名称、函数指针、函数参数、初始tick、循环tick、标志参数。当然,由于定时器分为单次定时和循环定时两种,所以这里会有两个参数,如果不是循环定时器,循环tick参数可以不用配置。
RAW_U16 raw_timer_activate(RAW_TIMER *timer_ptr) { RAW_U16 position; RAW_SR_ALLOC(); #if (RAW_TIMER_FUNCTION_CHECK > 0) if (timer_ptr == 0) { return RAW_NULL_OBJECT; } #endif /*Timer state TIMER_ACTIVE TIMER_DELETED is not allowed to delete*/ if (timer_ptr->timer_state == TIMER_ACTIVE) return RAW_TIMER_STATE_INVALID; if (timer_ptr->timer_state == TIMER_DELETED) return RAW_TIMER_STATE_INVALID; RAW_CRITICAL_ENTER(); timer_ptr->match = raw_timer_count + timer_ptr->init_count; position = (RAW_U16)(timer_ptr->match & (TIMER_HEAD_NUMBERS - 1) ); /*Sort by remain time*/ timer_ptr->remain = timer_ptr->init_count; /*Used by timer delete*/ timer_ptr->to_head = &timer_head[position]; timer_list_priority_insert(&timer_head[position], timer_ptr); timer_ptr->timer_state = TIMER_ACTIVE; RAW_CRITICAL_EXIT(); return RAW_SUCCESS; }
启动定时器,就是把tick加入到定时器队列当中,看看timer_list_priority_insert这个函数你就明白了。因为整个函数的处理过程涉及到 全局变量timer_head,所以需要在前后面添加中断的处理动作。
RAW_U16 raw_timer_change(RAW_TIMER *timer_ptr, RAW_U32 initial_ticks, RAW_U32 reschedule_ticks) { RAW_SR_ALLOC(); #if (RAW_TIMER_FUNCTION_CHECK > 0) if (timer_ptr == 0) { return RAW_NULL_OBJECT; } #endif /*Only timer state TIMER_DEACTIVE is not allowed here*/ if (timer_ptr->timer_state != TIMER_DEACTIVE) { return RAW_TIMER_STATE_INVALID; } if (timer_ptr->timer_state == TIMER_DELETED) { return RAW_TIMER_STATE_INVALID; } RAW_CRITICAL_ENTER(); timer_ptr->init_count = initial_ticks; timer_ptr->reschedule_ticks = reschedule_ticks; RAW_CRITICAL_EXIT(); return RAW_SUCCESS; }
定时器修改函数就是把初始tick和循环tick修改一下,当然如果此时定时器还没有运行,初始tick还有用。但是一旦定时器起作用了,起作用的就只能是循环tick了,这一点大家要好好注意。
RAW_U16 raw_timer_deactivate(RAW_TIMER *timer_ptr) { RAW_SR_ALLOC(); #if (RAW_TIMER_FUNCTION_CHECK > 0) if (timer_ptr == 0) { return RAW_NULL_OBJECT; } #endif /*Timer state TIMER_DEACTIVE TIMER_DELETED is not allowed to delete*/ if (timer_ptr->timer_state == TIMER_DEACTIVE) { return RAW_TIMER_STATE_INVALID; } if (timer_ptr->timer_state == TIMER_DELETED) { return RAW_TIMER_STATE_INVALID; } RAW_CRITICAL_ENTER(); timer_list_remove(timer_ptr); timer_ptr->timer_state = TIMER_DEACTIVE; RAW_CRITICAL_EXIT(); return RAW_SUCCESS; }
停止定时器,顾名思义就是将定时器从队列中删除,同时设置状态为TIMER_DEACTIVE。当然在进行操作之前需要判断一下当前定时器的属性,如果定时器本身已经删除或者停止,那什么也不用做了。
RAW_U16 raw_timer_delete(RAW_TIMER *timer_ptr) { RAW_SR_ALLOC(); #if (RAW_TIMER_FUNCTION_CHECK > 0) if (timer_ptr == 0) { return RAW_NULL_OBJECT; } #endif if (timer_ptr->timer_state == TIMER_DELETED) { return RAW_TIMER_STATE_INVALID; } RAW_CRITICAL_ENTER(); timer_list_remove(timer_ptr); timer_ptr->timer_state = TIMER_DELETED; RAW_CRITICAL_EXIT(); return RAW_SUCCESS; }
定时器的删除函数和定时器的停止函数是一样的,主要是判断逻辑不一样的。删除定时器,只要定时器的状态不是已经删除就可以了,其他的操作都是一样的。
事件
在很多操作系统的书上,其实互斥和同步是放在一起进行介绍的。互斥,比较简单,就是对某一份资源或者几份资源进行抢占获取。而同步是什么意思呢,就是某一个线程等待另外一个线程的通知,只有收到了通知,它才会去干某些事情。
通常情况下,如果是抢占的话,那么两个人使用的必须是同一个锁,而同步的话,则需要好几个锁,因为一般情况下大家等待的东西都是不一样的,所以好几个锁是不可避免的。那么,有没有什么办法,可以用一个锁实现几个事情的并发和同步呢?这就是我们今天所要说的事件。可以从一个例子说明一下。
比方说,我们现在打算进行八宝饭的烹饪。那么,在此之前需要进行各个辅料的准备工作,等到这些辅料都准备好了,就可以开始煮八宝饭了。因为辅料之间是相互独立的,所以完全可以分开独立完成,而在所有辅料都没有完成之前,我们只能等待。等到材料全部准备好,我们就可以开始烹饪的工作了。当然,在烹饪的时候,我们又可以准备进行下一轮工作了,也就是说进行下一次八宝饭的辅料准备。在这个地方,辅料的准备是由各个子线程完成的,而煮饭这个工作是主线程完成的,主线程和子线程之间就是通过事件进行沟通的。主线程需要知道当前各个材料准备好了没,而子线程需要知道八宝饭烧好了没,是不是该进行下一轮辅料的准备了。这个中间就存在一个同步的问题了。
如果大家对之前的信号量还有印象的话,当初我们是用count来表示资源的个数。而今天,我们用flags来表示事件状态,而其中的bit则表示了一个一个具体的事件。只不过有的线程在等待多个事件,而有的线程在等待一个事件,有的线程在获取事件后bit位立即清除,有的线程在获取事件后继续留存。
所以下面,我们就看看raw-os上面的事件是怎么设计的。当然,我们首先看到的还是关于事件的基本数据结构,
typedef struct RAW_EVENT { RAW_COMMON_BLOCK_OBJECT common_block_obj; RAW_U32 flags; } RAW_EVENT;
这和我们之前介绍的没什么不一样,就是通用结构加上flag标志。关于事件的基本处理函数也不复杂,主要就是创建、申请、设置和删除四个基本操作。我们来看看每一步分别是怎么实现的,首先介绍的还是事件的创建过程,
RAW_U16 raw_event_create(RAW_EVENT *event_ptr, RAW_U8 *name_ptr, RAW_U32 flags_init) { #if (RAW_EVENT_FUNCTION_CHECK > 0) if (event_ptr == 0) { return RAW_NULL_OBJECT; } #endif /*Init the list*/ list_init(&event_ptr->common_block_obj.block_list); event_ptr->common_block_obj.block_way = 0; event_ptr->common_block_obj.name = name_ptr; event_ptr->flags = flags_init ; return RAW_SUCCESS; }
看了代码,相信要说的部分不是很多,关键就是flags的赋值部分,其他的都和信号量差不太多。这里的flags代表了某一个起始状态,也就是说当前可以干什么事情、满足哪些条件等等。下面,我们继续看事件的获取函数,稍微复杂一些,
RAW_U16 raw_event_get(RAW_EVENT *event_ptr, RAW_U32 requested_flags, RAW_U8 get_option, RAW_U32 wait_option) { RAW_U16 error_status; RAW_U8 status; RAW_SR_ALLOC(); #if (RAW_EVENT_FUNCTION_CHECK > 0) if (raw_int_nesting) { return RAW_NOT_CALLED_BY_ISR; } if ((get_option != RAW_AND) && (get_option != RAW_OR) && (get_option != RAW_AND_CLEAR) && (get_option != RAW_OR_CLEAR)) { return RAW_NO_THIS_OPTION; } #endif RAW_CRITICAL_ENTER(); /*if option is and flag*/ if (get_option & RAW_FLAGS_AND_MASK) { if ((event_ptr->flags & requested_flags) == requested_flags) { status = RAW_TRUE; } else { status = RAW_FALSE; } } /*if option is or flag*/ else { if (event_ptr->flags & requested_flags) { status = RAW_TRUE; } else { status = RAW_FALSE; } } if (status) { /*does it need to clear the flags*/ if (get_option & RAW_FLAGS_CLEAR_MASK) { event_ptr->flags &= ~requested_flags; } RAW_CRITICAL_EXIT(); return RAW_SUCCESS; } /*Cann't get event, and return immediately if wait_option is RAW_NO_WAIT*/ if (wait_option == RAW_NO_WAIT) { RAW_CRITICAL_EXIT(); return RAW_NO_PEND_WAIT; } /*system is locked so task can not be blocked just return immediately*/ if (raw_sched_lock) { RAW_CRITICAL_EXIT(); return RAW_SCHED_DISABLE; } /*Remember the passed information*/ raw_task_active->raw_suspend_option = get_option; raw_task_active->raw_suspend_flags = requested_flags; raw_pend_object(&event_ptr->common_block_obj, raw_task_active, wait_option); RAW_CRITICAL_EXIT(); raw_sched(); RAW_CRITICAL_ENTER(); /*does it need to clear the flags*/ if (get_option & RAW_FLAGS_CLEAR_MASK) { event_ptr->flags &= ~requested_flags; } RAW_CRITICAL_EXIT(); /*So the task is waked up, need know which reason cause wake up.*/ error_status = block_state_post_process(raw_task_active, 0); return error_status; }
注意,这里事件和其他get函数的最大差别就是,函数多了一个get_option,它表示当前是同时申请多个事件还是多个事件中的一个事件,申请后是否需要进行clear置位等等,我们不妨看看具体细节,
(1)判断函数是否在中断中;
(2)判断get_option是否合法;
(3)判断是否存在可以获取的事件,and或者是or;
(4)如果事件可以获取,那么再判断是否需要置位操作,函数返回;
(5)判断是否愿意等待,否则返回;
(6)判断是否禁止调度,是则返回;
(7)将自己pend到等待队列中;
(8)调用公共调度函数转到其他线程继续运行;
(9)当前线程重新得到运行的机会,根据选项清除标志位,函数返回。
看完了事件的申请,下面就可以看看事件的设置函数了,
RAW_U16 raw_event_set(RAW_EVENT *event_ptr, RAW_U32 flags_to_set, RAW_U8 set_option) { LIST *iter; LIST *event_head_ptr; LIST *iter_temp; struct RAW_TASK_OBJ *task_ptr; RAW_U8 status; RAW_U8 need_sche = 0; RAW_SR_ALLOC(); #if (RAW_EVENT_FUNCTION_CHECK > 0) if (event_ptr == 0) { return RAW_NULL_OBJECT; } if ((set_option != RAW_AND) && (set_option != RAW_OR)) { return RAW_NO_THIS_OPTION; } #endif event_head_ptr = &event_ptr->common_block_obj.block_list; status = RAW_FALSE; RAW_CRITICAL_ENTER(); /*if the set_option is AND_MASK, it just clear the flags and will return immediately!*/ if (set_option & RAW_FLAGS_AND_MASK) { event_ptr->flags &= flags_to_set; RAW_CRITICAL_EXIT(); return RAW_SUCCESS; } /*if it is or mask then set the flag and continue.........*/ else { event_ptr->flags |= flags_to_set; } iter = event_head_ptr->next; /*if list is not empty*/ while (iter !=event_head_ptr) { task_ptr = list_entry(iter, RAW_TASK_OBJ, task_list); iter_temp = iter->next; if (task_ptr->raw_suspend_option & RAW_FLAGS_AND_MASK) { if ((event_ptr->flags & task_ptr ->raw_suspend_flags) == task_ptr ->raw_suspend_flags) status = RAW_TRUE; else status = RAW_FALSE; } else { if (event_ptr->flags & task_ptr ->raw_suspend_flags) status = RAW_TRUE; else status = RAW_FALSE; } if (status) { /*Ok the task condition is met, just wake this task*/ raw_wake_object(task_ptr); /*if task is waken up*/ need_sche = 1; } iter = iter_temp; } RAW_CRITICAL_EXIT(); if (need_sche) { raw_sched(); } return RAW_SUCCESS; }
从函数上也看得出来,这里有一个set_option的选项,主要是为了供调用者选择是进行and设置还是or设置,细节如下所示,
(1)判断参数合法性;
(2)判断set_option合法性;
(3)如果选项为and,在设置完flags之后函数返回;
(4)设置flags标志位,开始遍历每一个等待线程;
(5)如果存在合适的线程,不管是等待多个事件还是一个事件,都将它们唤醒,设置重新调度标志;
(6)如果重新调度标志为1,调用系统调度函数切换到其他线程运行;
(7)当前线程再次获取到运行的机会,函数返回。
转眼之间,我们就到了事件的删除过程了。其实事件的删除非常简单,它就是把所有的等待线程唤醒,就这么简单,不知道我说清楚了没?当然了,这中间可能会有高优先级的线程被加入到ready队列里面,所以重新schedule一下也是很有必要的。
RAW_U16 raw_event_delete(RAW_EVENT *event_ptr) { LIST *block_list_head; RAW_SR_ALLOC(); #if (RAW_EVENT_FUNCTION_CHECK > 0) if (event_ptr == 0) { return RAW_NULL_OBJECT; } #endif block_list_head = &event_ptr->common_block_obj.block_list; RAW_CRITICAL_ENTER(); /*All task blocked on this queue is waken up until list is empty*/ while (!is_list_empty(block_list_head)) { delete_pend_obj(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list)); } event_ptr->flags = 0; RAW_CRITICAL_EXIT(); raw_sched(); return RAW_SUCCESS; }
线程状态
从第一篇的os博客以来,谈了很多内容,有中断、切换、调度、内存、互斥和延时等等,但是线程的状态却没有涉及到,今天我们要好好说一说。说到线程的状态,按照一般的说法,主要包括就绪、延时、阻塞、阻塞超时四个状态。如果线程没有死亡的话,那么这几个状态也够用了,但是我们后来发现可能需要对某些线程进行挂起处理,这可能是出现了故障或者是为了调试使用。因此,除了上面的四个状态,我们还要补充对应的四个挂起状态,分别是挂起、延时挂起、阻塞挂起、阻塞延时挂起。
说到了线程状态,下面我们就看看常见的线程处理函数有哪些,无外乎线程创建、线程延时、线程挂起、线程恢复和线程删除等等。
RAW_U16 raw_task_create(RAW_TASK_OBJ *task_obj, RAW_U8 *task_name, RAW_VOID *task_arg, RAW_U8 task_prio, RAW_U16 time_slice, PORT_STACK *task_stack_base, RAW_U32 stack_size, RAW_TASK_ENTRY task_entry, RAW_U8 auto_start) { #if (RAW_TASK_STACK_CHECK > 0) PORT_STACK *p_stack; RAW_U32 i; #endif RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (task_obj == 0) { return RAW_NULL_OBJECT; } if (task_prio >= CONFIG_RAW_PRIO_MAX) { return RAW_BYOND_MAX_PRIORITY; } if (task_stack_base == 0) { return RAW_NULL_POINTER; } if (task_entry == 0) { return RAW_NULL_POINTER; } #endif RAW_CRITICAL_ENTER(); if (task_prio == IDLE_PRIORITY) { if (idle_task_exit) { RAW_CRITICAL_EXIT(); return RAW_IDLE_EXIT; } idle_task_exit = 1; } RAW_CRITICAL_EXIT(); raw_memset(task_obj, 0, sizeof(RAW_TASK_OBJ)); #if (CONFIG_ROUND_ROBIN > 0) if (time_slice) { task_obj->time_total = time_slice; } else { task_obj->time_total = TIME_SLICE_DEFAULT; } task_obj->time_slice = task_obj->time_total; #endif if (auto_start) task_obj->task_state = RAW_RDY; else task_obj->task_state = RAW_SUSPENDED; #if (RAW_TASK_STACK_CHECK > 0) task_obj->task_stack_base = task_stack_base; p_stack = task_stack_base; for (i = 0; i < stack_size; i++) { *p_stack++ =0; } #endif task_obj->task_stack = port_stack_init(task_stack_base, stack_size, task_arg, task_entry); task_obj->task_name = task_name; task_obj->priority = task_prio; task_create_hook(task_obj); RAW_CRITICAL_ENTER(); #if (RAW_TASK_STACK_CHECK > 0) task_obj->stack_size = stack_size; list_insert(&task_head, &task_obj->stack_check_list); #endif if (auto_start) { add_ready_list_end(&raw_ready_queue, task_obj); } if (raw_os_active != RAW_OS_RUNNING) { /* Return if multitasking has not started */ RAW_CRITICAL_EXIT(); return RAW_OS_STOPPED; } RAW_CRITICAL_EXIT(); if (auto_start) { raw_sched(); } return RAW_SUCCESS; }
创建线程的函数是比较复杂的,内容长一些,参数也多一些。首先看看有哪些参数,虽然很多,但是慢慢梳理一下也不难理解,有名称、参数、优先级、时间片、堆栈起始指针、堆栈大小、入口函数和标志。整个函数基本上都是赋值的过程,最重要的其实就两个部分,一个是port_stack_init,另一个就是add_ready_list_end。前者可以对堆栈进行默认处理,比如压入一些寄存器、压入函数参数、函数指针等等,后者就是把线程加入到就绪队列。
RAW_U16 raw_task_create(RAW_TASK_OBJ *task_obj, RAW_U8 *task_name, RAW_VOID *task_arg, RAW_U8 task_prio, RAW_U16 time_slice, PORT_STACK *task_stack_base, RAW_U32 stack_size, RAW_TASK_ENTRY task_entry, RAW_U8 auto_start) { #if (RAW_TASK_STACK_CHECK > 0) PORT_STACK *p_stack; RAW_U32 i; #endif RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (task_obj == 0) { return RAW_NULL_OBJECT; } if (task_prio >= CONFIG_RAW_PRIO_MAX) { return RAW_BYOND_MAX_PRIORITY; } if (task_stack_base == 0) { return RAW_NULL_POINTER; } if (task_entry == 0) { return RAW_NULL_POINTER; } #endif RAW_CRITICAL_ENTER(); if (task_prio == IDLE_PRIORITY) { if (idle_task_exit) { RAW_CRITICAL_EXIT(); return RAW_IDLE_EXIT; } idle_task_exit = 1; } RAW_CRITICAL_EXIT(); raw_memset(task_obj, 0, sizeof(RAW_TASK_OBJ)); #if (CONFIG_ROUND_ROBIN > 0) if (time_slice) { task_obj->time_total = time_slice; } else { task_obj->time_total = TIME_SLICE_DEFAULT; } task_obj->time_slice = task_obj->time_total; #endif if (auto_start) task_obj->task_state = RAW_RDY; else task_obj->task_state = RAW_SUSPENDED; #if (RAW_TASK_STACK_CHECK > 0) task_obj->task_stack_base = task_stack_base; p_stack = task_stack_base; for (i = 0; i < stack_size; i++) { *p_stack++ =0; } #endif task_obj->task_stack = port_stack_init(task_stack_base, stack_size, task_arg, task_entry); task_obj->task_name = task_name; task_obj->priority = task_prio; task_create_hook(task_obj); RAW_CRITICAL_ENTER(); #if (RAW_TASK_STACK_CHECK > 0) task_obj->stack_size = stack_size; list_insert(&task_head, &task_obj->stack_check_list); #endif if (auto_start) { add_ready_list_end(&raw_ready_queue, task_obj); } if (raw_os_active != RAW_OS_RUNNING) { /* Return if multitasking has not started */ RAW_CRITICAL_EXIT(); return RAW_OS_STOPPED; } RAW_CRITICAL_EXIT(); if (auto_start) { raw_sched(); } return RAW_SUCCESS; }
我们之前也介绍过系统的延时功能。延时,就是把线程暂时从就绪队列清除出来,添加到延时队列中。当然如果参数为0,那表示作者只是希望暂时释放cpu的使用权,如果此时没有同等优先级的任务,那么下一个运行的线程还是它自己。
RAW_U16 raw_task_suspend(RAW_TASK_OBJ *task_ptr) { RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (task_ptr == 0) { return RAW_NULL_OBJECT; } #endif if (task_ptr->priority == IDLE_PRIORITY) { return RAW_SUSPEND_TASK_NOT_ALLOWED; } RAW_CRITICAL_ENTER(); if (task_ptr == raw_task_active) { if (raw_sched_lock) { RAW_CRITICAL_EXIT(); return RAW_SCHED_LOCKED; } } switch (task_ptr->task_state) { case RAW_RDY: task_ptr->task_state = RAW_SUSPENDED; remove_ready_list(&raw_ready_queue, task_ptr); break; case RAW_DLY: task_ptr->task_state = RAW_DLY_SUSPENDED; break; case RAW_PEND: task_ptr->task_state = RAW_PEND_SUSPENDED; break; case RAW_PEND_TIMEOUT: task_ptr->task_state = RAW_PEND_TIMEOUT_SUSPENDED; break; case RAW_DLY_SUSPENDED: case RAW_PEND_SUSPENDED: case RAW_PEND_TIMEOUT_SUSPENDED: RAW_CRITICAL_EXIT(); return RAW_SUSPENDED_AGAIN; default: #if (CONFIG_RAW_ASSERT > 0) RAW_ASSERT(0); #endif RAW_CRITICAL_EXIT(); return RAW_STATE_UNKNOWN; } RAW_CRITICAL_EXIT(); raw_sched(); return RAW_SUCCESS; }
挂起任务的动作其实是比较残暴的,因为此时你不知道线程处于什么状态。当然任务如果已经被挂起了,那什么也不用做了,否则就需要把任务修改为对应的挂起状态就可以了。当然如果任务是就绪态的,还得把任务清除处理来。在函数结束的时候,我们需要重新进行调度,因为很有可能当前最高优先级的线程已经发生了改变。
RAW_U16 raw_task_resume(RAW_TASK_OBJ *task_ptr) { RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (task_ptr == 0) { return RAW_NULL_OBJECT; } #endif RAW_CRITICAL_ENTER(); switch (task_ptr->task_state) { case RAW_RDY: case RAW_DLY: case RAW_PEND: case RAW_PEND_TIMEOUT: RAW_CRITICAL_EXIT(); return HAS_NOT_SUSPENDED; case RAW_SUSPENDED: task_ptr->task_state = RAW_RDY; add_ready_list(&raw_ready_queue, task_ptr); break; case RAW_DLY_SUSPENDED: task_ptr->task_state = RAW_DLY; break; case RAW_PEND_SUSPENDED: task_ptr->task_state = RAW_PEND; break; case RAW_PEND_TIMEOUT_SUSPENDED: task_ptr->task_state = RAW_PEND_TIMEOUT; break; default: #if (CONFIG_RAW_ASSERT > 0) RAW_ASSERT(0); #endif RAW_CRITICAL_EXIT(); return RAW_STATE_UNKNOWN; } RAW_CRITICAL_EXIT(); raw_sched(); return RAW_SUCCESS; }
恢复函数其实就是挂起函数的逆向操作。如果任务没有被挂起,那么什么也不用做。否则就需要把任务的状态修改为对应的非挂起状态,当然该就绪的线程还得加入到就绪队列当中去。同时在函数结束之前不忘调度一下,说不定刚刚释放的这个线程就是优先级最高的那个线程。
RAW_U16 raw_task_delete(RAW_TASK_OBJ *task_ptr) { RAW_SR_ALLOC(); #if (RAW_TASK_FUNCTION_CHECK > 0) if (task_ptr == 0) { return RAW_NULL_OBJECT; } if (raw_int_nesting) { return RAW_NOT_CALLED_BY_ISR; } #endif if (task_ptr->priority == IDLE_PRIORITY) { return RAW_DELETE_TASK_NOT_ALLOWED; } RAW_CRITICAL_ENTER(); if (task_ptr == raw_task_active) { if (raw_sched_lock) { RAW_CRITICAL_EXIT(); return RAW_SCHED_LOCKED; } } switch (task_ptr->task_state) { case RAW_RDY: remove_ready_list(&raw_ready_queue, task_ptr); break; case RAW_SUSPENDED: break; case RAW_DLY: /* Task is only delayed, not on any wait list */ case RAW_DLY_SUSPENDED: tick_list_remove(task_ptr); break; case RAW_PEND: case RAW_PEND_SUSPENDED: case RAW_PEND_TIMEOUT: case RAW_PEND_TIMEOUT_SUSPENDED: tick_list_remove(task_ptr); list_delete(&task_ptr->task_list); break; default: #if (CONFIG_RAW_ASSERT > 0) RAW_ASSERT(0); #endif RAW_CRITICAL_EXIT(); return RAW_STATE_UNKNOWN; } task_ptr->task_state = RAW_DELETED; #if (RAW_TASK_STACK_CHECK > 0) /*make after_delete_list to right position*/ after_delete_list = task_ptr->stack_check_list.next; if (after_delete_list == &task_head) { after_delete_list = task_head.next; } list_delete(&task_ptr->stack_check_list); #endif RAW_CRITICAL_EXIT(); raw_sched(); return RAW_SUCCESS; }
删除函数的动作其实是比较残忍的,因为此时你不清楚线程已经执行到哪一步了,拥有了那些资源,正在处理哪些资源,所以没事不要用这个函数。这里做的只是把任务从就绪队列、等待队列和阻塞队列清除出来,但是真正善后的工作要比这多得多,如果有兴趣,你看看linux的exit函数就明白了。
参考资料:https://blog.csdn.net/feixiaoxing/article/details/8050193