延时操作

延时操作是操作系统中经常遇到的一种情形。延时的原因很多,有的时候是为了等待外设芯片处理结束,有的时候是为了暂时释放cpu的使用权,有的就是为了希望在一段时间获取资源,如果没法在单位时间内获取,放弃等待。但是不管怎么说,延时都是操作系统必不可少的一个工作。下面我们就看看延时是怎么实现的,

  1. static void tick_list_priority_insert(LIST *head, RAW_TASK_OBJ *task_ptr)
  2. {
  3.   RAW_U32 val;
  4.  
  5.   LIST *q,*start, *end;
  6.   RAW_TASK_OBJ *task_iter_temp;
  7.  
  8.   start = end = head;
  9.   val = task_ptr->tick_remain;
  10.  
  11.   for (q = start->next; q != end; q = q->next) {
  12.  
  13.     task_iter_temp = list_entry(q, RAW_TASK_OBJ, tick_list);
  14.  
  15.     /*sorted by remain time*/
  16.  
  17.     if ((task_iter_temp->tick_match - raw_tick_count) > val) {
  18.       break;
  19.     }
  20.   }
  21.  
  22.   list_insert(q, &task_ptr->tick_list);
  23.   
  24.  
  25. }
  26.  
  27. void tick_list_insert(RAW_TASK_OBJ *task_ptr, RAW_U32 time)
  28.  
  29. {
  30.   LIST *tick_head_ptr;
  31.  
  32.   RAW_U16 spoke;
  33.  
  34.   if (time) {
  35.  
  36.     task_ptr->tick_match = raw_tick_count + time;
  37.     task_ptr->tick_remain = time;
  38.  
  39.     spoke = (RAW_U16)(task_ptr->tick_match & (TICK_HEAD_ARRAY - 1) );
  40.     tick_head_ptr = &tick_head[spoke];
  41.   
  42.     tick_list_priority_insert(tick_head_ptr, task_ptr);
  43.  
  44.     task_ptr->tick_head = tick_head_ptr;
  45.  
  46.   }
  47.  
  48. }

延时的代码其实不是很多,所以我在这里把最重要的两个函数给粘贴到这里了。因为每个线程都有可能延时,那么怎么处理这些线程之间的关系就是我们需要做的事情了。我们看到了,我们直接用tick_match表示线程需要等待的那个时间点就可以了。当然,tick是不断增加的,我们可以把尾数相同的线程按照高低顺序排列在一起,这样在对应的tick到来的时候,就直接按照尾数查找就可以了,tick_list_priority_insert就是干了这么一件事情。
 
     那么,tick什么时候到期呢?到期又该怎么处理呢,我们接着往下看,

  1. void tick_list_update(void)
  2. {
  3.  
  4.   LIST *tick_head_ptr;
  5.   RAW_TASK_OBJ *p_tcb;
  6.   LIST *iter;
  7.   LIST *iter_temp;
  8.  
  9.   RAW_U16 spoke;
  10.  
  11.   RAW_SR_ALLOC();
  12.  
  13.   RAW_CRITICAL_ENTER();
  14.  
  15.   raw_tick_count++;
  16.   spoke = (RAW_U16)(raw_tick_count & (TICK_HEAD_ARRAY - 1) );
  17.   tick_head_ptr = &tick_head[spoke];
  18.   iter = tick_head_ptr->next;
  19.  
  20.   while (RAW_TRUE) {
  21.  
  22.     /*search all the time list if possible*/
  23.     if (iter != tick_head_ptr) {
  24.  
  25.       iter_temp = iter->next;
  26.       p_tcb = list_entry(iter, RAW_TASK_OBJ, tick_list);
  27.  
  28.         /*Since time list is sorted by remain time, so just campare the absolute time*/
  29.         if (raw_tick_count == p_tcb->tick_match) {
  30.  
  31.           switch (p_tcb->task_state) {
  32.             case RAW_DLY:
  33.  
  34.               p_tcb->block_status = RAW_B_OK;
  35.               p_tcb->task_state = RAW_RDY;
  36.               tick_list_remove(p_tcb);
  37.               add_ready_list(&raw_ready_queue, p_tcb);
  38.               break;
  39.  
  40.             case RAW_PEND_TIMEOUT:
  41.  
  42.               p_tcb->block_status = RAW_B_TIMEOUT;
  43.               p_tcb->task_state = RAW_RDY;
  44.               p_tcb->block_obj = 0;
  45.               tick_list_remove(p_tcb);
  46.               /*remove task on the block list because task is timeout*/
  47.               list_delete(&p_tcb->task_list);
  48.               add_ready_list(&raw_ready_queue, p_tcb);
  49.               break;
  50.  
  51.             case RAW_PEND_TIMEOUT_SUSPENDED:
  52.  
  53.               p_tcb->block_status = RAW_B_TIMEOUT;
  54.               p_tcb->task_state = RAW_SUSPENDED;
  55.               p_tcb->block_obj = 0;
  56.               tick_list_remove(p_tcb);
  57.               /*remove task on the block list because task is timeout*/
  58.               list_delete(&p_tcb->task_list);
  59.               break;
  60.  
  61.             case RAW_DLY_SUSPENDED:
  62.  
  63.               p_tcb->task_state = RAW_SUSPENDED;
  64.               p_tcb->block_status = RAW_B_OK;
  65.               tick_list_remove(p_tcb);
  66.               break;
  67.  
  68.             default:
  69.  
  70.               #if (CONFIG_RAW_ASSERT > 0)
  71.                 RAW_ASSERT(0);
  72.               #endif
  73.  
  74.               break;
  75.           }
  76.  
  77.           iter = iter_temp;
  78.         }
  79.  
  80.         /*if current task time out absolute time is not equal current system time, just break because timer list is sorted*/
  81.         else {
  82.  
  83.           break;
  84.  
  85.         }
  86.  
  87.       }
  88.  
  89.       /*finish all the time list search */
  90.  
  91.       else {
  92.  
  93.         break;
  94.       }
  95.  
  96.     }
  97.  
  98.     RAW_CRITICAL_EXIT();
  99. }

这个函数是在时钟中断的时候被调用的,根据函数的先后顺序,看看函数实现了哪些功能,
     (1)自增raw_tick_count;
     (2)根据尾数获取tick队列的头指针;
     (3)开始循环迭代处理延时线程;
             a)如果没有没有延时线程,循环跳出;
             b)如果线程的终点tick和当前tick不匹配,跳出循环,因为tick都是排序好的,所以后面的tick肯定不满足要求;
             c)如果当前tick满足要求,根据线程状态进行处理,主要分为延时、阻塞超时、延时挂起、阻塞超时挂起四种状态;
             d)获取下一个延时线程,观察是否满足要求,如果是继续回到c,否则退出循环。
     (4)函数返回,继续时钟中断的剩余操作。
 
     最后,我们补充一下关于有限时间等待的知识。就像以前关于互斥操作的内容一样,其实某些情况下,我们是有时间限制的。一段时间没有获取资源,我们就不希望等待了,所以这里的延时操作还包括这部分的内容,我们看看阻塞函数的相关代码就明白了。

  1. RAW_U16 raw_pend_object(RAW_COMMON_BLOCK_OBJECT *block_common_obj, RAW_TASK_OBJ *task_ptr, RAW_U32 timeout)
  2. {
  3.  
  4.   #if (CONFIG_RAW_ASSERT > 0)
  5.  
  6.   if (timeout == 0) {
  7.     RAW_ASSERT(0);
  8.   }
  9.  
  10.   #endif
  11.  
  12.   task_ptr->block_obj = block_common_obj;
  13.  
  14.   if (timeout == RAW_WAIT_FOREVER) {
  15.  
  16.     task_ptr->task_state = RAW_PEND;
  17.  
  18.   }
  19.   /*task is blocked with timeout*/
  20.   else {
  21.  
  22.   tick_list_insert(task_ptr,timeout);
  23.  
  24.     task_ptr->task_state = RAW_PEND_TIMEOUT;
  25.  
  26.   }
  27.  
  28.   /*Remove from the ready list*/
  29.   remove_ready_list(&raw_ready_queue, task_ptr);
  30.  
  31.   if (block_common_obj->block_way == RAW_BLOCKED_WAY_FIFO) {
  32.  
  33.     list_insert(&block_common_obj->block_list, &task_ptr->task_list);
  34.  
  35.   }
  36.  
  37.   else {
  38.  
  39.     /*add to the priority sorted block list*/
  40.     add_to_priority_list(&block_common_obj->block_list, task_ptr);
  41.   
  42.   }
  43.  
  44.   return RAW_SUCCESS;
  45. }

大家留意一下这里timeout参数的处理过程,关注一下对应的tick_list_insert函数,这样就可以明白我的意思了。

实时系统中的定时器

关于定时器的内容,其实我们之前也讨论过,也书写过相应的代码,但是表达得比较晦涩,效率也比较低。所以我们这里重新再讲一下定时器的相关代码,看看嵌入式系统中的定时器是怎么实现的。在我们之前讨论线程延时的时候就使用hash的方法,将不同的线程归类到不同的延时队列当中,并且按照时间长短先后排列,这样在最短的时间内就可以寻找到最合适的线程了。本质上,线程延时和定时器的基本原理是一样的。唯一的区别就是,线程延时响应的优先级要高一些,而定时器一般由独立线程完成,rawos也是这么做的。

  1. void timer_task(void *pa)
  2. {
  3.   RAW_U16 position;
  4.   LIST *timer_head_ptr;
  5.   LIST *iter;
  6.   LIST *iter_temp;
  7.   RAW_TIMER *timer_ptr;
  8.  
  9.   timer_sem.count = 0;
  10.  
  11.   while (1) {
  12.  
  13.     /*timer task will be blocked after call this function*/
  14.     raw_semaphore_get(&timer_sem, RAW_WAIT_FOREVER);
  15.  
  16.     /*Disable the system schedule we do not need disable interrupt since nothing to do with interrupt*/
  17.     raw_disable_sche();
  18.  
  19.     /*calculate which timer_head*/
  20.     raw_timer_count++;
  21.     position = (RAW_U16)(raw_timer_count & (TIMER_HEAD_NUMBERS - 1) );
  22.     timer_head_ptr = &timer_head[position];
  23.  
  24.     iter =timer_head_ptr->next;
  25.  
  26.     while (RAW_TRUE) {
  27.       /*if timer exits*/
  28.       if (iter !=timer_head_ptr) {
  29.         /*Must use iter_temp because iter may be remove later.*/
  30.         iter_temp = iter->next;
  31.         timer_ptr = list_entry(iter, RAW_TIMER, timer_list);
  32.  
  33.         /*if timeout*/
  34.         if (raw_timer_count == timer_ptr->match) {
  35.  
  36.           /*remove form timer list*/
  37.           timer_list_remove(timer_ptr);
  38.           /*if timer is reschedulable*/
  39.           if (timer_ptr->reschedule_ticks) {
  40.             /*Sort by remain time*/
  41.             timer_ptr->remain = timer_ptr->reschedule_ticks;
  42.  
  43.             timer_ptr->match = raw_timer_count + timer_ptr->remain;
  44.             position = (RAW_U16)(timer_ptr->match & (TIMER_HEAD_NUMBERS - 1));
  45.             timer_ptr->to_head = &timer_head[position];
  46.             timer_list_priority_insert(&timer_head[position], timer_ptr);
  47.  
  48.           }
  49.  
  50.           /*Any way both condition need to call registered timer function*/
  51.  
  52.           if (timer_ptr->raw_timeout_function) {
  53.             timer_ptr->raw_timeout_function(timer_ptr->raw_timeout_param);
  54.  
  55.           }
  56.  
  57.           iter = iter_temp;
  58.         }
  59.   
  60.         else {
  61.  
  62.           break;
  63.  
  64.         }
  65.  
  66.       }
  67.       /*exit because timer is not exit*/
  68.       else {
  69.  
  70.         break;
  71.       }
  72.  
  73.     }
  74.  
  75.     raw_enable_sche();
  76.   }
  77. }

由于基本原理和之前的线程延时是一样的,所以这里就不重复了。定时器的基本操作其实也不多,主要包括定时器创建、启动定时器、修改定时器、关闭定时器、删除定时器共五个基本函数,大家可以和我一起慢慢看过来。

  1. RAW_U16 raw_timer_create(RAW_TIMER *timer_ptr, RAW_U8 *name_ptr,
  2.   RAW_VOID (*expiration_function)(RAW_U32), RAW_U32 expiration_input,
  3.   RAW_U32 initial_ticks, RAW_U32 reschedule_ticks, RAW_U8 auto_activate)
  4.  
  5. {
  6.  
  7.     #if (RAW_TIMER_FUNCTION_CHECK > 0)
  8.  
  9.     if (timer_ptr == 0) {
  10.       return RAW_NULL_OBJECT;
  11.     }
  12.  
  13.     if (expiration_function == 0) {
  14.       return RAW_NULL_POINTER;
  15.     }
  16.  
  17.     #endif
  18.  
  19.     timer_ptr->name = name_ptr;
  20.     timer_ptr->raw_timeout_function = expiration_function;
  21.     timer_ptr->raw_timeout_param = expiration_input;
  22.     timer_ptr->init_count = initial_ticks;
  23.     timer_ptr->reschedule_ticks = reschedule_ticks;
  24.     timer_ptr->remain = 0;
  25.     timer_ptr->match = 0;
  26.     timer_ptr->timer_state = TIMER_DEACTIVE;
  27.     timer_ptr->to_head = 0;
  28.  
  29.     list_init(&timer_ptr->timer_list);
  30.  
  31.     if (auto_activate) {
  32.  
  33.       raw_timer_activate(timer_ptr);
  34.     }
  35.  
  36.     return RAW_SUCCESS;
  37. } 

创建定时器的操作很简单,主要是把输入的参数存入到RAW_TIMER结构里面。相关的参数包括名称、函数指针、函数参数、初始tick、循环tick、标志参数。当然,由于定时器分为单次定时和循环定时两种,所以这里会有两个参数,如果不是循环定时器,循环tick参数可以不用配置。

  1. RAW_U16 raw_timer_activate(RAW_TIMER *timer_ptr)
  2. {
  3.   RAW_U16 position;
  4.   RAW_SR_ALLOC();
  5.  
  6.   #if (RAW_TIMER_FUNCTION_CHECK > 0)
  7.  
  8.   if (timer_ptr == 0) {
  9.     return RAW_NULL_OBJECT;
  10.   }
  11.  
  12.   #endif
  13.  
  14.   /*Timer state TIMER_ACTIVE TIMER_DELETED is not allowed to delete*/
  15.   if (timer_ptr->timer_state == TIMER_ACTIVE)
  16.     return RAW_TIMER_STATE_INVALID;
  17.  
  18.   if (timer_ptr->timer_state == TIMER_DELETED)
  19.     return RAW_TIMER_STATE_INVALID;
  20.  
  21.   RAW_CRITICAL_ENTER();
  22.   timer_ptr->match = raw_timer_count + timer_ptr->init_count;
  23.   position = (RAW_U16)(timer_ptr->match & (TIMER_HEAD_NUMBERS - 1) );
  24.   /*Sort by remain time*/
  25.   timer_ptr->remain = timer_ptr->init_count;
  26.   /*Used by timer delete*/
  27.   timer_ptr->to_head = &timer_head[position];
  28.  
  29.   timer_list_priority_insert(&timer_head[position], timer_ptr);
  30.   timer_ptr->timer_state = TIMER_ACTIVE;
  31.  
  32.   RAW_CRITICAL_EXIT();
  33.  
  34.   return RAW_SUCCESS;
  35. }

启动定时器,就是把tick加入到定时器队列当中,看看timer_list_priority_insert这个函数你就明白了。因为整个函数的处理过程涉及到  全局变量timer_head,所以需要在前后面添加中断的处理动作。

  1. RAW_U16 raw_timer_change(RAW_TIMER *timer_ptr, RAW_U32 initial_ticks, RAW_U32 reschedule_ticks)
  2. {
  3.   RAW_SR_ALLOC();
  4.  
  5.   #if (RAW_TIMER_FUNCTION_CHECK > 0)
  6.  
  7.   if (timer_ptr == 0) {
  8.     return RAW_NULL_OBJECT;
  9.   }
  10.  
  11.   #endif
  12.  
  13.   /*Only timer state TIMER_DEACTIVE is not allowed here*/
  14.   if (timer_ptr->timer_state != TIMER_DEACTIVE) {
  15.     return RAW_TIMER_STATE_INVALID;
  16.   }
  17.  
  18.   if (timer_ptr->timer_state == TIMER_DELETED) {
  19.     return RAW_TIMER_STATE_INVALID;
  20.   }
  21.  
  22.   RAW_CRITICAL_ENTER();
  23.   timer_ptr->init_count = initial_ticks;
  24.   timer_ptr->reschedule_ticks = reschedule_ticks;
  25.   RAW_CRITICAL_EXIT();
  26.   return RAW_SUCCESS;
  27. }

定时器修改函数就是把初始tick和循环tick修改一下,当然如果此时定时器还没有运行,初始tick还有用。但是一旦定时器起作用了,起作用的就只能是循环tick了,这一点大家要好好注意。

  1. RAW_U16 raw_timer_deactivate(RAW_TIMER *timer_ptr)
  2. {
  3.   RAW_SR_ALLOC();
  4.  
  5.   #if (RAW_TIMER_FUNCTION_CHECK > 0)
  6.  
  7.   if (timer_ptr == 0) {
  8.     return RAW_NULL_OBJECT;
  9.   }
  10.  
  11.   #endif
  12.  
  13.   /*Timer state TIMER_DEACTIVE TIMER_DELETED is not allowed to delete*/
  14.   if (timer_ptr->timer_state == TIMER_DEACTIVE) {
  15.     return RAW_TIMER_STATE_INVALID;
  16.   }
  17.  
  18.   if (timer_ptr->timer_state == TIMER_DELETED) {
  19.     return RAW_TIMER_STATE_INVALID;
  20.  
  21.   }
  22.  
  23.   RAW_CRITICAL_ENTER();
  24.   timer_list_remove(timer_ptr);
  25.   timer_ptr->timer_state = TIMER_DEACTIVE;
  26.   RAW_CRITICAL_EXIT();
  27.  
  28.   return RAW_SUCCESS;
  29.  
  30. }

停止定时器,顾名思义就是将定时器从队列中删除,同时设置状态为TIMER_DEACTIVE。当然在进行操作之前需要判断一下当前定时器的属性,如果定时器本身已经删除或者停止,那什么也不用做了。

  1. RAW_U16 raw_timer_delete(RAW_TIMER *timer_ptr)
  2. {
  3.   RAW_SR_ALLOC();
  4.  
  5.   #if (RAW_TIMER_FUNCTION_CHECK > 0)
  6.  
  7.   if (timer_ptr == 0) {
  8.     return RAW_NULL_OBJECT;
  9.   }
  10.  
  11.   #endif
  12.  
  13.   if (timer_ptr->timer_state == TIMER_DELETED) {
  14.  
  15.     return RAW_TIMER_STATE_INVALID;
  16.  
  17.   }
  18.  
  19.   RAW_CRITICAL_ENTER();
  20.  
  21.   timer_list_remove(timer_ptr);
  22.   timer_ptr->timer_state = TIMER_DELETED;
  23.  
  24.   RAW_CRITICAL_EXIT();
  25.  
  26.   return RAW_SUCCESS;
  27.  
  28. }

定时器的删除函数和定时器的停止函数是一样的,主要是判断逻辑不一样的。删除定时器,只要定时器的状态不是已经删除就可以了,其他的操作都是一样的。

事件

在很多操作系统的书上,其实互斥和同步是放在一起进行介绍的。互斥,比较简单,就是对某一份资源或者几份资源进行抢占获取。而同步是什么意思呢,就是某一个线程等待另外一个线程的通知,只有收到了通知,它才会去干某些事情。

通常情况下,如果是抢占的话,那么两个人使用的必须是同一个锁,而同步的话,则需要好几个锁,因为一般情况下大家等待的东西都是不一样的,所以好几个锁是不可避免的。那么,有没有什么办法,可以用一个锁实现几个事情的并发和同步呢?这就是我们今天所要说的事件。可以从一个例子说明一下。

比方说,我们现在打算进行八宝饭的烹饪。那么,在此之前需要进行各个辅料的准备工作,等到这些辅料都准备好了,就可以开始煮八宝饭了。因为辅料之间是相互独立的,所以完全可以分开独立完成,而在所有辅料都没有完成之前,我们只能等待。等到材料全部准备好,我们就可以开始烹饪的工作了。当然,在烹饪的时候,我们又可以准备进行下一轮工作了,也就是说进行下一次八宝饭的辅料准备。在这个地方,辅料的准备是由各个子线程完成的,而煮饭这个工作是主线程完成的,主线程和子线程之间就是通过事件进行沟通的。主线程需要知道当前各个材料准备好了没,而子线程需要知道八宝饭烧好了没,是不是该进行下一轮辅料的准备了。这个中间就存在一个同步的问题了。

如果大家对之前的信号量还有印象的话,当初我们是用count来表示资源的个数。而今天,我们用flags来表示事件状态,而其中的bit则表示了一个一个具体的事件。只不过有的线程在等待多个事件,而有的线程在等待一个事件,有的线程在获取事件后bit位立即清除,有的线程在获取事件后继续留存。

所以下面,我们就看看raw-os上面的事件是怎么设计的。当然,我们首先看到的还是关于事件的基本数据结构,

  1. typedef struct RAW_EVENT
  2. {
  3.   RAW_COMMON_BLOCK_OBJECT common_block_obj;
  4.   RAW_U32 flags;
  5. } RAW_EVENT;

这和我们之前介绍的没什么不一样,就是通用结构加上flag标志。关于事件的基本处理函数也不复杂,主要就是创建、申请、设置和删除四个基本操作。我们来看看每一步分别是怎么实现的,首先介绍的还是事件的创建过程,

  1. RAW_U16 raw_event_create(RAW_EVENT *event_ptr, RAW_U8 *name_ptr, RAW_U32 flags_init)
  2. {
  3.   #if (RAW_EVENT_FUNCTION_CHECK > 0)
  4.   if (event_ptr == 0) {
  5.     return RAW_NULL_OBJECT;
  6.   }
  7.   #endif
  8.   /*Init the list*/
  9.   list_init(&event_ptr->common_block_obj.block_list);
  10.   event_ptr->common_block_obj.block_way = 0;
  11.   event_ptr->common_block_obj.name = name_ptr;
  12.   event_ptr->flags = flags_init ;
  13.  
  14.   return RAW_SUCCESS;
  15. }

看了代码,相信要说的部分不是很多,关键就是flags的赋值部分,其他的都和信号量差不太多。这里的flags代表了某一个起始状态,也就是说当前可以干什么事情、满足哪些条件等等。下面,我们继续看事件的获取函数,稍微复杂一些,

  1. RAW_U16 raw_event_get(RAW_EVENT *event_ptr, RAW_U32 requested_flags, RAW_U8 get_option, RAW_U32 wait_option)
  2. {
  3.   RAW_U16 error_status;
  4.  
  5.   RAW_U8 status;
  6.   RAW_SR_ALLOC();
  7.  
  8.   #if (RAW_EVENT_FUNCTION_CHECK > 0)
  9.  
  10.   if (raw_int_nesting) {
  11.     return RAW_NOT_CALLED_BY_ISR;
  12.   }
  13.   if ((get_option != RAW_AND) && (get_option != RAW_OR) && (get_option != RAW_AND_CLEAR) && (get_option != RAW_OR_CLEAR)) {
  14.     return RAW_NO_THIS_OPTION;
  15.   }
  16.  
  17.   #endif
  18.  
  19.   RAW_CRITICAL_ENTER();
  20.   /*if option is and flag*/
  21.   if (get_option & RAW_FLAGS_AND_MASK) {
  22.     if ((event_ptr->flags & requested_flags) == requested_flags) {
  23.       status = RAW_TRUE;
  24.     }
  25.     else {
  26.       status = RAW_FALSE;
  27.     }
  28.  
  29.   }
  30.   /*if option is or flag*/
  31.   else {
  32.     if (event_ptr->flags & requested_flags) {
  33.       status = RAW_TRUE;
  34.     }
  35.     else {
  36.       status = RAW_FALSE;
  37.     }
  38.   }
  39.   if (status) {
  40.     /*does it need to clear the flags*/
  41.     if (get_option & RAW_FLAGS_CLEAR_MASK) {
  42.       event_ptr->flags &= ~requested_flags;
  43.     }
  44.     RAW_CRITICAL_EXIT();
  45.     return RAW_SUCCESS;
  46.   }
  47.   /*Cann't get event, and return immediately if wait_option is RAW_NO_WAIT*/
  48.   if (wait_option == RAW_NO_WAIT) {
  49.     RAW_CRITICAL_EXIT();
  50.     return RAW_NO_PEND_WAIT;
  51.   }
  52.  
  53.   /*system is locked so task can not be blocked just return immediately*/
  54.   if (raw_sched_lock) {
  55.     RAW_CRITICAL_EXIT();
  56.     return RAW_SCHED_DISABLE;
  57.   }
  58.   /*Remember the passed information*/
  59.   raw_task_active->raw_suspend_option = get_option;
  60.   raw_task_active->raw_suspend_flags = requested_flags;
  61.   raw_pend_object(&event_ptr->common_block_obj, raw_task_active, wait_option);
  62.   RAW_CRITICAL_EXIT();
  63.   raw_sched();
  64.   RAW_CRITICAL_ENTER();
  65.   /*does it need to clear the flags*/
  66.   if (get_option & RAW_FLAGS_CLEAR_MASK) {
  67.     event_ptr->flags &= ~requested_flags;
  68.   }
  69.  
  70.   RAW_CRITICAL_EXIT();
  71.   /*So the task is waked up, need know which reason cause wake up.*/
  72.   error_status = block_state_post_process(raw_task_active, 0);
  73.   return error_status;
  74. }

注意,这里事件和其他get函数的最大差别就是,函数多了一个get_option,它表示当前是同时申请多个事件还是多个事件中的一个事件,申请后是否需要进行clear置位等等,我们不妨看看具体细节,
    (1)判断函数是否在中断中;

(2)判断get_option是否合法;

(3)判断是否存在可以获取的事件,and或者是or;

(4)如果事件可以获取,那么再判断是否需要置位操作,函数返回;

(5)判断是否愿意等待,否则返回;

(6)判断是否禁止调度,是则返回;

(7)将自己pend到等待队列中;

(8)调用公共调度函数转到其他线程继续运行;

(9)当前线程重新得到运行的机会,根据选项清除标志位,函数返回。

看完了事件的申请,下面就可以看看事件的设置函数了,

  1. RAW_U16 raw_event_set(RAW_EVENT *event_ptr, RAW_U32 flags_to_set, RAW_U8 set_option)
  2. {
  3.   LIST *iter;
  4.   LIST *event_head_ptr;
  5.   LIST *iter_temp;
  6.   struct RAW_TASK_OBJ *task_ptr;
  7.  
  8.   RAW_U8 status;
  9.   RAW_U8 need_sche = 0;
  10.  
  11.   RAW_SR_ALLOC();
  12.  
  13.   #if (RAW_EVENT_FUNCTION_CHECK > 0)
  14.  
  15.   if (event_ptr == 0) {
  16.     return RAW_NULL_OBJECT;
  17.   }
  18.  
  19.   if ((set_option != RAW_AND) && (set_option != RAW_OR)) {
  20.     return RAW_NO_THIS_OPTION;
  21.   }
  22.  
  23.   #endif
  24.  
  25.   event_head_ptr = &event_ptr->common_block_obj.block_list;
  26.  
  27.   status = RAW_FALSE;
  28.  
  29.   RAW_CRITICAL_ENTER();
  30.  
  31.   /*if the set_option is AND_MASK, it just clear the flags and will return immediately!*/
  32.   if (set_option & RAW_FLAGS_AND_MASK) {
  33.  
  34.     event_ptr->flags &= flags_to_set;
  35.  
  36.     RAW_CRITICAL_EXIT();
  37.     return RAW_SUCCESS;
  38.   }
  39.   /*if it is or mask then set the flag and continue.........*/
  40.   else {
  41.  
  42.     event_ptr->flags |= flags_to_set;
  43.   }
  44.   iter = event_head_ptr->next;
  45.   /*if list is not empty*/
  46.   while (iter !=event_head_ptr) {
  47.     task_ptr = list_entry(iter, RAW_TASK_OBJ, task_list);
  48.     iter_temp = iter->next;
  49.     if (task_ptr->raw_suspend_option & RAW_FLAGS_AND_MASK) {
  50.       if ((event_ptr->flags & task_ptr ->raw_suspend_flags) == task_ptr ->raw_suspend_flags)
  51.         status = RAW_TRUE;
  52.       else
  53.         status = RAW_FALSE;
  54.     }
  55.     else {
  56.       if (event_ptr->flags & task_ptr ->raw_suspend_flags)
  57.         status = RAW_TRUE;
  58.       else
  59.         status = RAW_FALSE;
  60.     }
  61.     if (status) {
  62.  
  63.       /*Ok the task condition is met, just wake this task*/
  64.       raw_wake_object(task_ptr);
  65.       /*if task is waken up*/
  66.       need_sche = 1;
  67.     }
  68.     iter = iter_temp;
  69.   }
  70.   RAW_CRITICAL_EXIT();
  71.  
  72.   if (need_sche) {
  73.     raw_sched();
  74.   }
  75.   return RAW_SUCCESS;
  76. }

从函数上也看得出来,这里有一个set_option的选项,主要是为了供调用者选择是进行and设置还是or设置,细节如下所示,
    (1)判断参数合法性;

(2)判断set_option合法性;

(3)如果选项为and,在设置完flags之后函数返回;

(4)设置flags标志位,开始遍历每一个等待线程;

(5)如果存在合适的线程,不管是等待多个事件还是一个事件,都将它们唤醒,设置重新调度标志;

(6)如果重新调度标志为1,调用系统调度函数切换到其他线程运行;

(7)当前线程再次获取到运行的机会,函数返回。

转眼之间,我们就到了事件的删除过程了。其实事件的删除非常简单,它就是把所有的等待线程唤醒,就这么简单,不知道我说清楚了没?当然了,这中间可能会有高优先级的线程被加入到ready队列里面,所以重新schedule一下也是很有必要的。

  1. RAW_U16 raw_event_delete(RAW_EVENT *event_ptr)
  2. {
  3.   LIST *block_list_head;
  4.  
  5.   RAW_SR_ALLOC();
  6.  
  7.   #if (RAW_EVENT_FUNCTION_CHECK > 0)
  8.  
  9.   if (event_ptr == 0) {
  10.     return RAW_NULL_OBJECT;
  11.   }
  12.  
  13.   #endif
  14.  
  15.   block_list_head = &event_ptr->common_block_obj.block_list;
  16.  
  17.   RAW_CRITICAL_ENTER();
  18.  
  19.   /*All task blocked on this queue is waken up until list is empty*/
  20.   while (!is_list_empty(block_list_head)) {
  21.     delete_pend_obj(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list));
  22.   }
  23.  
  24.   event_ptr->flags = 0;
  25.  
  26.   RAW_CRITICAL_EXIT();
  27.  
  28.   raw_sched();
  29.  
  30.   return RAW_SUCCESS;
  31. }

  

线程状态

从第一篇的os博客以来,谈了很多内容,有中断、切换、调度、内存、互斥和延时等等,但是线程的状态却没有涉及到,今天我们要好好说一说。说到线程的状态,按照一般的说法,主要包括就绪、延时、阻塞、阻塞超时四个状态。如果线程没有死亡的话,那么这几个状态也够用了,但是我们后来发现可能需要对某些线程进行挂起处理,这可能是出现了故障或者是为了调试使用。因此,除了上面的四个状态,我们还要补充对应的四个挂起状态,分别是挂起、延时挂起、阻塞挂起、阻塞延时挂起。

说到了线程状态,下面我们就看看常见的线程处理函数有哪些,无外乎线程创建、线程延时、线程挂起、线程恢复和线程删除等等。

  1. RAW_U16 raw_task_create(RAW_TASK_OBJ *task_obj, RAW_U8 *task_name, RAW_VOID *task_arg,
  2. RAW_U8 task_prio, RAW_U16 time_slice, PORT_STACK *task_stack_base,
  3. RAW_U32 stack_size, RAW_TASK_ENTRY task_entry, RAW_U8 auto_start)
  4.  
  5. {
  6.     #if (RAW_TASK_STACK_CHECK > 0)
  7.     PORT_STACK *p_stack;
  8.     RAW_U32 i;
  9.     #endif
  10.  
  11.     RAW_SR_ALLOC();
  12.  
  13.     #if (RAW_TASK_FUNCTION_CHECK > 0)
  14.  
  15.     if (task_obj == 0) {
  16.     return RAW_NULL_OBJECT;
  17.     }
  18.  
  19.     if (task_prio >= CONFIG_RAW_PRIO_MAX) {
  20.       return RAW_BYOND_MAX_PRIORITY;
  21.     }
  22.  
  23.     if (task_stack_base == 0) {
  24.       return RAW_NULL_POINTER;
  25.     }
  26.  
  27.     if (task_entry == 0) {
  28.       return RAW_NULL_POINTER;
  29.     }
  30.  
  31.     #endif
  32.  
  33.     RAW_CRITICAL_ENTER();
  34.  
  35.     if (task_prio == IDLE_PRIORITY) {
  36.  
  37.     if (idle_task_exit) {
  38.  
  39.       RAW_CRITICAL_EXIT();
  40.       return RAW_IDLE_EXIT;
  41.  
  42.     }
  43.  
  44.     idle_task_exit = 1;
  45.   }
  46.  
  47.   RAW_CRITICAL_EXIT();
  48.  
  49.   raw_memset(task_obj, 0, sizeof(RAW_TASK_OBJ));
  50.  
  51.   #if (CONFIG_ROUND_ROBIN > 0)
  52.  
  53.   if (time_slice) {
  54.     task_obj->time_total = time_slice;
  55.  
  56.   }
  57.  
  58.   else {
  59.  
  60.     task_obj->time_total = TIME_SLICE_DEFAULT;
  61.   }
  62.   
  63.   task_obj->time_slice = task_obj->time_total;
  64.  
  65.   #endif
  66.  
  67.   if (auto_start)
  68.     task_obj->task_state = RAW_RDY;
  69.   else
  70.     task_obj->task_state = RAW_SUSPENDED;
  71.  
  72.   #if (RAW_TASK_STACK_CHECK > 0)
  73.   
  74.     task_obj->task_stack_base = task_stack_base;
  75.   p_stack = task_stack_base;
  76.  
  77.   for (i = 0; i < stack_size; i++) {
  78.     *p_stack++ =0;
  79.  
  80.   }
  81.  
  82.   #endif
  83.  
  84.   task_obj->task_stack = port_stack_init(task_stack_base, stack_size, task_arg, task_entry);
  85.   task_obj->task_name = task_name;
  86.   task_obj->priority = task_prio;
  87.  
  88.   task_create_hook(task_obj);
  89.   
  90.  
  91.   RAW_CRITICAL_ENTER();
  92.  
  93.   #if (RAW_TASK_STACK_CHECK > 0)
  94.   task_obj->stack_size = stack_size;
  95.   list_insert(&task_head, &task_obj->stack_check_list);
  96.   #endif
  97.  
  98.   if (auto_start) {
  99.     add_ready_list_end(&raw_ready_queue, task_obj);
  100.   }
  101.  
  102.   if (raw_os_active != RAW_OS_RUNNING) { /* Return if multitasking has not started */
  103.     RAW_CRITICAL_EXIT();
  104.     return RAW_OS_STOPPED;
  105.   }
  106.  
  107.   RAW_CRITICAL_EXIT();
  108.  
  109.   if (auto_start) {
  110.     raw_sched();
  111.   }
  112.  
  113.   return RAW_SUCCESS;
  114.  
  115. }

  

创建线程的函数是比较复杂的,内容长一些,参数也多一些。首先看看有哪些参数,虽然很多,但是慢慢梳理一下也不难理解,有名称、参数、优先级、时间片、堆栈起始指针、堆栈大小、入口函数和标志。整个函数基本上都是赋值的过程,最重要的其实就两个部分,一个是port_stack_init,另一个就是add_ready_list_end。前者可以对堆栈进行默认处理,比如压入一些寄存器、压入函数参数、函数指针等等,后者就是把线程加入到就绪队列。

  1. RAW_U16 raw_task_create(RAW_TASK_OBJ *task_obj, RAW_U8 *task_name, RAW_VOID *task_arg,
  2. RAW_U8 task_prio, RAW_U16 time_slice, PORT_STACK *task_stack_base,
  3. RAW_U32 stack_size, RAW_TASK_ENTRY task_entry, RAW_U8 auto_start)
  4.  
  5. {
  6.   #if (RAW_TASK_STACK_CHECK > 0)
  7.   PORT_STACK *p_stack;
  8.   RAW_U32 i;
  9.   #endif
  10.  
  11.   RAW_SR_ALLOC();
  12.  
  13.   #if (RAW_TASK_FUNCTION_CHECK > 0)
  14.  
  15.   if (task_obj == 0) {
  16.     return RAW_NULL_OBJECT;
  17.   }
  18.  
  19.   if (task_prio >= CONFIG_RAW_PRIO_MAX) {
  20.     return RAW_BYOND_MAX_PRIORITY;
  21.   }
  22.  
  23.   if (task_stack_base == 0) {
  24.     return RAW_NULL_POINTER;
  25.   }
  26.  
  27.   if (task_entry == 0) {
  28.     return RAW_NULL_POINTER;
  29.   }
  30.  
  31.   #endif
  32.  
  33.   RAW_CRITICAL_ENTER();
  34.  
  35.   if (task_prio == IDLE_PRIORITY) {
  36.  
  37.       if (idle_task_exit) {
  38.  
  39.       RAW_CRITICAL_EXIT();
  40.       return RAW_IDLE_EXIT;
  41.  
  42.       }
  43.  
  44.   idle_task_exit = 1;
  45.   }
  46.  
  47.   RAW_CRITICAL_EXIT();
  48.  
  49.   raw_memset(task_obj, 0, sizeof(RAW_TASK_OBJ));
  50.   
  51.   #if (CONFIG_ROUND_ROBIN > 0)
  52.  
  53.   if (time_slice) {
  54.     task_obj->time_total = time_slice;
  55.  
  56.   }
  57.  
  58.   else {
  59.  
  60.   task_obj->time_total = TIME_SLICE_DEFAULT;
  61.   }
  62.  
  63.   task_obj->time_slice = task_obj->time_total;
  64.  
  65.   #endif
  66.  
  67.   if (auto_start)
  68.     task_obj->task_state = RAW_RDY;
  69.   else
  70.     task_obj->task_state = RAW_SUSPENDED;
  71.  
  72.   #if (RAW_TASK_STACK_CHECK > 0)
  73.  
  74.   task_obj->task_stack_base = task_stack_base;
  75.   p_stack = task_stack_base;
  76.  
  77.   for (i = 0; i < stack_size; i++) {
  78.     *p_stack++ =0;
  79.  
  80.   }
  81.  
  82.   #endif
  83.  
  84.   task_obj->task_stack = port_stack_init(task_stack_base, stack_size, task_arg, task_entry);
  85.   task_obj->task_name = task_name;
  86.   task_obj->priority = task_prio;
  87.  
  88.   task_create_hook(task_obj);
  89.  
  90.   RAW_CRITICAL_ENTER();
  91.  
  92.   #if (RAW_TASK_STACK_CHECK > 0)
  93.   task_obj->stack_size = stack_size;
  94.   list_insert(&task_head, &task_obj->stack_check_list);
  95.   #endif
  96.  
  97.   if (auto_start) {
  98.     add_ready_list_end(&raw_ready_queue, task_obj);
  99.   }
  100.  
  101.   if (raw_os_active != RAW_OS_RUNNING) { /* Return if multitasking has not started */
  102.     RAW_CRITICAL_EXIT();
  103.     return RAW_OS_STOPPED;
  104.   }
  105.  
  106.   RAW_CRITICAL_EXIT();
  107.  
  108.   if (auto_start) {
  109.     raw_sched();
  110.   }
  111.  
  112.   return RAW_SUCCESS;
  113.  
  114. }

  

我们之前也介绍过系统的延时功能。延时,就是把线程暂时从就绪队列清除出来,添加到延时队列中。当然如果参数为0,那表示作者只是希望暂时释放cpu的使用权,如果此时没有同等优先级的任务,那么下一个运行的线程还是它自己。

  1. RAW_U16 raw_task_suspend(RAW_TASK_OBJ *task_ptr)
  2. {
  3. RAW_SR_ALLOC();
  4.  
  5. #if (RAW_TASK_FUNCTION_CHECK > 0)
  6.  
  7. if (task_ptr == 0) {
  8. return RAW_NULL_OBJECT;
  9. }
  10.  
  11. #endif
  12.  
  13. if (task_ptr->priority == IDLE_PRIORITY) {
  14. return RAW_SUSPEND_TASK_NOT_ALLOWED;
  15. }
  16.  
  17. RAW_CRITICAL_ENTER();
  18.  
  19. if (task_ptr == raw_task_active) {
  20.  
  21. if (raw_sched_lock) {
  22. RAW_CRITICAL_EXIT();
  23. return RAW_SCHED_LOCKED;
  24. }
  25. }
  26.  
  27. switch (task_ptr->task_state) {
  28. case RAW_RDY:
  29. task_ptr->task_state = RAW_SUSPENDED;
  30. remove_ready_list(&raw_ready_queue, task_ptr);
  31. break;
  32.  
  33. case RAW_DLY:
  34. task_ptr->task_state = RAW_DLY_SUSPENDED;
  35. break;
  36.  
  37. case RAW_PEND:
  38.  
  39. task_ptr->task_state = RAW_PEND_SUSPENDED;
  40. break;
  41.  
  42. case RAW_PEND_TIMEOUT:
  43. task_ptr->task_state = RAW_PEND_TIMEOUT_SUSPENDED;
  44. break;
  45.  
  46. case RAW_DLY_SUSPENDED:
  47. case RAW_PEND_SUSPENDED:
  48. case RAW_PEND_TIMEOUT_SUSPENDED:
  49. RAW_CRITICAL_EXIT();
  50. return RAW_SUSPENDED_AGAIN;
  51.  
  52. default:
  53.  
  54. #if (CONFIG_RAW_ASSERT > 0)
  55. RAW_ASSERT(0);
  56. #endif
  57.  
  58. RAW_CRITICAL_EXIT();
  59. return RAW_STATE_UNKNOWN;
  60. }
  61.  
  62. RAW_CRITICAL_EXIT();
  63.  
  64. raw_sched();
  65.  
  66. return RAW_SUCCESS;
  67. }

  

挂起任务的动作其实是比较残暴的,因为此时你不知道线程处于什么状态。当然任务如果已经被挂起了,那什么也不用做了,否则就需要把任务修改为对应的挂起状态就可以了。当然如果任务是就绪态的,还得把任务清除处理来。在函数结束的时候,我们需要重新进行调度,因为很有可能当前最高优先级的线程已经发生了改变。

  1. RAW_U16 raw_task_resume(RAW_TASK_OBJ *task_ptr)
  2. {
  3. RAW_SR_ALLOC();
  4.  
  5. #if (RAW_TASK_FUNCTION_CHECK > 0)
  6.  
  7. if (task_ptr == 0) {
  8. return RAW_NULL_OBJECT;
  9. }
  10.  
  11. #endif
  12.  
  13. RAW_CRITICAL_ENTER();
  14.  
  15. switch (task_ptr->task_state) {
  16. case RAW_RDY:
  17. case RAW_DLY:
  18. case RAW_PEND:
  19. case RAW_PEND_TIMEOUT:
  20.  
  21. RAW_CRITICAL_EXIT();
  22. return HAS_NOT_SUSPENDED;
  23.  
  24. case RAW_SUSPENDED:
  25. task_ptr->task_state = RAW_RDY;
  26. add_ready_list(&raw_ready_queue, task_ptr);
  27. break;
  28.  
  29. case RAW_DLY_SUSPENDED:
  30.  
  31. task_ptr->task_state = RAW_DLY;
  32. break;
  33.  
  34. case RAW_PEND_SUSPENDED:
  35.  
  36. task_ptr->task_state = RAW_PEND;
  37. break;
  38.  
  39. case RAW_PEND_TIMEOUT_SUSPENDED:
  40.  
  41. task_ptr->task_state = RAW_PEND_TIMEOUT;
  42. break;
  43.  
  44. default:
  45.  
  46. #if (CONFIG_RAW_ASSERT > 0)
  47. RAW_ASSERT(0);
  48. #endif
  49.  
  50. RAW_CRITICAL_EXIT();
  51.  
  52. return RAW_STATE_UNKNOWN;
  53.  
  54. }
  55.  
  56. RAW_CRITICAL_EXIT();
  57.  
  58. raw_sched();
  59.  
  60. return RAW_SUCCESS;
  61. }

  

恢复函数其实就是挂起函数的逆向操作。如果任务没有被挂起,那么什么也不用做。否则就需要把任务的状态修改为对应的非挂起状态,当然该就绪的线程还得加入到就绪队列当中去。同时在函数结束之前不忘调度一下,说不定刚刚释放的这个线程就是优先级最高的那个线程。

  1. RAW_U16 raw_task_delete(RAW_TASK_OBJ *task_ptr)
  2. {
  3.   RAW_SR_ALLOC();
  4.  
  5.   #if (RAW_TASK_FUNCTION_CHECK > 0)
  6.  
  7.   if (task_ptr == 0) {
  8.  
  9.     return RAW_NULL_OBJECT;
  10.   }
  11.  
  12.   if (raw_int_nesting) {
  13.  
  14.     return RAW_NOT_CALLED_BY_ISR;
  15.  
  16.   }
  17.  
  18.   #endif
  19.  
  20.   if (task_ptr->priority == IDLE_PRIORITY) {
  21.  
  22.     return RAW_DELETE_TASK_NOT_ALLOWED;
  23.   }
  24.  
  25.   RAW_CRITICAL_ENTER();
  26.  
  27.   if (task_ptr == raw_task_active) {
  28.  
  29.     if (raw_sched_lock) {
  30.       RAW_CRITICAL_EXIT();
  31.       return RAW_SCHED_LOCKED;
  32.     }
  33.   }
  34.  
  35.   switch (task_ptr->task_state) {
  36.     case RAW_RDY:
  37.       remove_ready_list(&raw_ready_queue, task_ptr);
  38.       break;
  39.  
  40.     case RAW_SUSPENDED:
  41.       break;
  42.  
  43.     case RAW_DLY: /* Task is only delayed, not on any wait list */
  44.     case RAW_DLY_SUSPENDED:
  45.       tick_list_remove(task_ptr);
  46.       break;
  47.  
  48.     case RAW_PEND:
  49.     case RAW_PEND_SUSPENDED:
  50.     case RAW_PEND_TIMEOUT:
  51.     case RAW_PEND_TIMEOUT_SUSPENDED:
  52.       tick_list_remove(task_ptr);
  53.       list_delete(&task_ptr->task_list);
  54.       break;
  55.  
  56.     default:
  57.  
  58.       #if (CONFIG_RAW_ASSERT > 0)
  59.       RAW_ASSERT(0);
  60.       #endif
  61.  
  62.       RAW_CRITICAL_EXIT();
  63.    return RAW_STATE_UNKNOWN;
  64.   }
  65.  
  66.   task_ptr->task_state = RAW_DELETED;
  67.  
  68.   #if (RAW_TASK_STACK_CHECK > 0)
  69.   /*make after_delete_list to right position*/
  70.   after_delete_list = task_ptr->stack_check_list.next;
  71.  
  72.   if (after_delete_list == &task_head) {
  73.     after_delete_list = task_head.next;
  74.   }
  75.  
  76.   list_delete(&task_ptr->stack_check_list);
  77.  
  78.   #endif
  79.  
  80.   RAW_CRITICAL_EXIT();
  81.  
  82.   raw_sched();
  83.  
  84.   return RAW_SUCCESS;
  85. }

  

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

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

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

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

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

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

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

  3. Linux系统学习笔记:文件I/O

    Linux支持C语言中的标准I/O函数,同时它还提供了一套SUS标准的I/O库函数.和标准I/O不同,UNIX的I/O函数是不带缓冲的,即每个读写都调用内核中的一个系统调用.本篇总结UNIX的I/O并 ...

  4. Linux系统基础命令

    这是看itercast的学习笔记 Linux系统基础命令 日期时间 命令date用以查看.设置当前系统时间:格式化显示时间: +%Y--%m--%d 命令hwclock(clock)用以显示硬件时钟时 ...

  5. Linu之linux系统基础优化和基本命令

    Linux系统基础优化和基本命令 网络参数设定命令 ifconfig: 查询,设置网卡和ip等参数 ifup,ifdown: 脚本命令,更简单的方式 ip: 符合指令,直接修改上述功能 编辑网卡配置文 ...

  6. (转)Linux系统基础网络配置老鸟精华篇

    Linux系统基础网络配置老鸟精华篇 原文:http://blog.51cto.com/oldboy/784625 对于linux高手看似简单的网络配置问题,也许要说出所以然来也并不轻松,因此仍然有太 ...

  7. Linux 系统基础优化和常用命令

    目录 Linux 系统基础优化和常用命令 软连接 tar解压命令 gzip命令 netstart命令 ps命令 kill命令 killall命令 SELinux功能 iptables防火墙 Linux ...

  8. 运维 07 Linux系统基础优化及常用命令

    Linux系统基础优化及常用命令   Linux基础系统优化 引言没有,只有一张图. Linux的网络功能相当强悍,一时之间我们无法了解所有的网络命令,在配置服务器基础环境时,先了解下网络参数设定命令 ...

  9. Linux—系统基础一

    Linux系统基础(一) Linux的基本原则: 由目的单一的小程序组成,组合小程序完成复杂任务: 一切皆文件: 配置文件保存为纯文本格式. 1.shell 1.1 shell简介 Shell俗称壳( ...

随机推荐

  1. 基于单细胞测序数据构建细胞状态转换轨迹(cell trajectory)方法总结

    细胞状态转换轨迹构建示意图(Trapnell et al. Nature Biotechnology, 2014) 在各种生物系统中,细胞都会展现出一系列的不同状态(如基因表达的动态变化等),这些状态 ...

  2. instanceof运算符与引用变量的强制类型转换

    一.instanceof运算符 instanceof是Java语言中的一个二元运算符,它的作用是判断一个引用类型的变量所指向的对象是否是一个类(或接口.抽象类.父类)的实例,即它左边的对象是否是它右边 ...

  3. String对象为什么不可变

    转载:https://www.cnblogs.com/leskang/p/6110631.html 一.什么是不可变对象? As we all know, 在Java中, String类对象是不可变的 ...

  4. Python学习之String

    Strings可以想象成一个有序列的数组 #Indexing planet = 'Pluto' planet[0] 'P' #Slicing planet[-3:] 'uto' #How long l ...

  5. Python 基础 3 - 元组

    元组与列表区别 Python 元组与列表类似,不同之处在于列表可以修改,元组不可以修改 元组用小括号 () 定义,列表用方括号 [] 定义 元组不可修改,列表可修改 元组创建 只需要在小括号 () 内 ...

  6. 让Jenkins执行GitHub上的pipeline脚本

    本文是<Jenkins流水线(pipeline)实战>系列的第二篇,上一篇搭建好了Jenkins环境并执行了一个简单的pipeline任务,当时我们直接在Jenkins网页上编写pipel ...

  7. <xsl:apply-templates>和<xsl:call-template>的区别

    <xsl:apply-templates> 应用模板,故名思意,将定义好的模板应用到 XML 的节点上.  可以调用 XML 文档的节点,使 XSL 文档可以渲染 XML 元素内的数据,  ...

  8. HTML5有哪些新特性,移除了哪些元素?如何处理HTML5新标签的浏览器兼容性问题?如何区分HTML和HTML5?

    HTML5现在已经不是SGML的子集,主要是关于图像,位置,存储,多任务等功能的增加. 绘画canvas: 用于媒介回放的video和audio元素: 本地离线存储localStorage长期存储数据 ...

  9. SqlServer还原数据库时提示:异常终止,不能在此版本的SQL Server中启动,因为它包含分区函数

    场景 在SqlServer Management中进行数据库还原时提示: 数据库不能在此版本的SQL Server中启动,因为它包含分区函数. 点击左下角的查看详细信息 实现 电脑上安装的是SQL S ...

  10. 松软科技课堂:SQL之NOTNULL约束

    SQL NOT NULL 约束 NOT NULL 约束强制列不接受 NULL 值. NOT NULL 约束强制字段始终包含值.这意味着,如果不向字段添加值,就无法插入新记录或者更新记录. 下面的 SQ ...