延时操作

延时操作是操作系统中经常遇到的一种情形。延时的原因很多,有的时候是为了等待外设芯片处理结束,有的时候是为了暂时释放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);#endifbreak;}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);}#endiftask_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;}#endiftimer_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;}#endifif (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;}#endifRAW_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;}#endifevent_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;elsestatus = RAW_FALSE;}else {if (event_ptr->flags & task_ptr ->raw_suspend_flags)status = RAW_TRUE;elsestatus = 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;}  #endifblock_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;#endifRAW_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;}#endifRAW_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;#endifif (auto_start)task_obj->task_state = RAW_RDY;elsetask_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; }#endiftask_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);#endifif (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;#endifRAW_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;}#endifRAW_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;#endifif (auto_start)task_obj->task_state = RAW_RDY;elsetask_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; }#endiftask_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);#endifif (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);
#endifRAW_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);
#endifRAW_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;}#endifif (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);#endifRAW_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);#endifRAW_CRITICAL_EXIT();raw_sched(); return RAW_SUCCESS;
}

  

删除函数的动作其实是比较残忍的,因为此时你不清楚线程已经执行到哪一步了,拥有了那些资源,正在处理哪些资源,所以没事不要用这个函数。这里做的只是把任务从就绪队列、等待队列和阻塞队列清除出来,但是真正善后的工作要比这多得多,如果有兴趣,你看看linux的exit函数就明白了。

参考资料:https://blog.csdn.net/feixiaoxing/article/details/8050193

转载于:https://www.cnblogs.com/still-smile/p/11609469.html

2.linux系统基础笔记(延时操作、实时系统中的定时器、事件)相关推荐

  1. 1.linux系统基础笔记(互斥量、信号量)

    操作系统是很多人每天必须打交道的东西,因为在你打开电脑的一刹那,随着bios自检结束,你的windows系统已经开始运行了.如果问大家操作系统是什么?可能有的人会说操作系统就是windows,就是那些 ...

  2. Linux系统基础小白学习(更新中)

    Linux系统小白学习 Linux基础介绍安装 1.1 Unix与Linux 背景 Unix&Linux区别与联系 1.2 Linux介绍 特点 组成 Linux文件系统 内核版本 硬件 文件 ...

  3. Linux实战教学笔记06:Linux系统基础优化

    第六节 Linux系统基础优化 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 基础环境 第2章 使用网易163镜像做yum源 默认国外的yum源速度很慢,所以换成国内的. 第一步:先备份 ...

  4. linux系统学习笔记6——debian系统分屏操作(亲测)

    debian系统分屏 刚插上HDMI链接屏幕后,发现两个屏幕是复制的效果 `步骤` xrandr | grep connecteda 1.查看现有的屏幕型号 由上图可以看到,一个是eDP-1 分辨率1 ...

  5. Linux C基础笔记(1)

    Linux C基础笔记(1) [1]四个重要的人物 肯.汤姆森:B语言发明者,C语言发明者,UNIX发明者,AT&T 丹尼斯.里奇:C语言之父,UNIX 之父 理查德.斯托曼:自由软件之父,l ...

  6. Linux系统编程笔记(李慧琴) 2

    Linux系统编程笔记(李慧琴) 02系统IO介绍 02系统IO介绍 I/O:input & output, 是一切实现的基础,主要分为 stdio 标准IO (所有的标准都是为了和稀泥整合不 ...

  7. 嵌入式Linux系统开发笔记(七)

    嵌入式Linux系统开发笔记(七) 七.U-Boot基础 1.概述 在移植 Linux之前我们需要先移植一个 bootloader 代码,这个 bootloader 代码用于启动 Linux 内核, ...

  8. linux系统下io的过程,Linux系统基础知识:IO调度

    Linux系统基础知识:IO调度 IO调度发生在Linux内核的IO调度层.这个层次是针对Linux的整体IO层次体系来说的.从read()或者write()系统调用的角度来说,Linux整体IO体系 ...

  9. linux 系统优化基础,Linux系统基础优化总结

    请称呼我搬运工,哈哈 优化综合 https://www.cnblogs.com/yinshoucheng-golden/p/6149556.html (1)不用root管理,以普通用户的名义通过sud ...

最新文章

  1. CSS画基本图形——圆
  2. ADO的几种数据库连接方式
  3. PCM设备能在公网使用吗?
  4. cocos2d-x android 环境搭配,cocos2d-x Android环境配置问题和解决方法
  5. android 最新 support,android support v7 下载-android support.v7包 官方最新版 - 河东下载站...
  6. GaussDB(for MySQL) HTAP只读分析特性详解
  7. jdbc数据库配置mysql数据库_JDBC连接MySQL数据库(一)——数据库的基本连接
  8. 数据分析没价值?——深思对业务的洞察有多少?
  9. esxi5.1 添加vSwitch,转VM network to new vSwitch
  10. 【python】pyhton中的and
  11. nodejs项目实例房屋租赁管理系统VUE.js
  12. 玩通了这4本书,也就掌握了SEO的精髓了
  13. 【魔兽世界插件】魔兽世界插件实战笔记从入门到放弃的心理历程 第六节 窗体对象的锚定
  14. 交换机路由器常用命令大全
  15. OA系统实现(请假审批,mybatis)-1
  16. 查询起止时间为同一天,需要查到当天数据
  17. 【AI测试】人工智能测试整体介绍——第二部分
  18. Structure of Heap
  19. 浅谈MYSQL增量备份
  20. Android 本地路径问题

热门文章

  1. Coin-row problem(1139)
  2. SqlBulkCopy转载
  3. centos 减少tty数量的方法
  4. 《你必须知道的.NET》第五章读书笔记
  5. SGI opengl source code download
  6. Linux下服务器搭建(7)——Oracle Linux ISO Images 高速镜像源下载地址(各版本齐全 建议收藏 最后更新2020.07.22)
  7. centos不同版本修改主机名的正确方法
  8. 原理简单,但不知道怎么用?一文看懂「同期群模型」
  9. 探索数据可视化,业务数据是核心
  10. 关于java垃圾回收器(GC)的一些基础知识