• <acronym id="mubfz"></acronym>

    <track id="mubfz"><ruby id="mubfz"></ruby></track>
    <object id="mubfz"><form id="mubfz"></form></object>

    1. <big id="mubfz"></big>
      <track id="mubfz"></track>

    2. 鸿蒙内核源码分析之时钟节拍的实现方式

      来源:my.oschina 作者: 鸿蒙内核源码分析2021-04-25 15:13:42
      [导读] 时钟节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次:void OsTickHandler(void),通知操作系统已经过去一个系统时钟;不同硬件定时器中断实现都不同,

      时钟概念

      时间是非常重要的概念,我们整个学生阶段有个东西很重要,就是校园铃声. 它控制着上课,下课,吃饭,睡觉的节奏.没有它学校的管理就乱套了,老师拖课想拖多久就多久,那可不行,下课铃声一响就是在告诉老师时间到了,该停止了让学生HAPPY去了.

      操作系统也一样,需要通过时间来规范其任务的执行,操作系统中最小的时间单位是时钟节拍 (OS Tick)。任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的实时响应越快,但是系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。

      在鸿蒙内核中,时钟节拍的长度可以根据 LOSCFG_BASE_CORE_TICK_PER_SECOND 的定义来调整,等于 1/LOSCFG_BASE_CORE_TICK_PER_SECOND 秒。

      时钟节拍的实现方式

      时钟节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次:void OsTickHandler(void),通知操作系统已经过去一个系统时钟;不同硬件定时器中断实现都不同,

      /**
       * @ingroup los_config
       * Number of Ticks in one second
       */
      #ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND
      #define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //默认每秒100次触发,当然这是可以改的
      #endif

      每秒100个tick,时间单位为10毫秒, 即每秒调用时钟中断处理程序100次.

      /*
       * Description : Tick interruption handler
       *///节拍中断处理函数 ,鸿蒙默认10ms触发一次
      LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
      {
          //...
          OsTimesliceCheck();//进程和任务的时间片检查
          OsTaskScan(); /* task timeout scan *///任务扫描
      #if (LOSCFG_BASE_CORE_SWTMR == YES)
          OsSwtmrScan();//定时器扫描,看是否有超时的定时器
      #endif
      }
      

      它主要干了三件事情

      第一:检查当前任务的时间片,任务执行一次分配多少时间呢?答案是2个时间片,即 20ms.

      #ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
      #define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2 //2个时间片,20ms
      #endif
      //检查进程和任务的时间片,如果没有时间片了直接调度
      LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID)
      {
          LosTaskCB *runTask = NULL;
          LosProcessCB *runProcess = OsCurrProcessGet();//获取当前进程
          if (runProcess->policy != LOS_SCHED_RR) {//进程调度算法是否是抢占式
              goto SCHED_TASK;//进程不是抢占式调度直接去检查任务的时间片
          }
      
          if (runProcess->timeSlice != 0) {//进程还有时间片吗?
              runProcess->timeSlice--;//进程时间片减少一次
              if (runProcess->timeSlice == 0) {//没有时间片了
                  LOS_Schedule();//进程时间片用完,发起调度
              }
          }
      
      SCHED_TASK:
          runTask = OsCurrTaskGet();//获取当前任务
          if (runTask->policy != LOS_SCHED_RR) {//任务调度算法是否是抢占式
              return;//任务不是抢占式调度直接结束检查
          }
      
          if (runTask->timeSlice != 0) {//任务还有时间片吗?
              runTask->timeSlice--;//任务时间片也减少一次
              if (runTask->timeSlice == 0) {//没有时间片了
                  LOS_Schedule();//任务时间片用完,发起调度
              }
          }
      }
      

      第二:扫描任务,主要是检查被阻塞的任务是否可以被重新调度

      LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
      {
          SortLinkList *sortList = NULL;
          LosTaskCB *taskCB = NULL;
          BOOL needSchedule = FALSE;
          UINT16 tempStatus;
          LOS_DL_LIST *listObject = NULL;
          SortLinkAttribute *taskSortLink = NULL;
      
          taskSortLink = &OsPercpuGet()->taskSortLink;//获取任务的排序链表
          taskSortLink->cursor = (taskSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
          listObject = taskSortLink->sortLink + taskSortLink->cursor;//只处理这个游标上的链表,因为系统对超时任务都已经规链表了.
       //当任务因超时而挂起时,任务块处于超时排序链接上,(每个cpu)和ipc(互斥锁、扫描电镜等)的块同时被唤醒
          /*不管是超时还是相应的ipc,它都在等待。现在使用synchronize sortlink precedure,因此整个任务扫描需要?;?,防止另一个核心同时删除sortlink。
           * When task is pended with timeout, the task block is on the timeout sortlink
           * (per cpu) and ipc(mutex,sem and etc.)'s block at the same time, it can be waken
           * up by either timeout or corresponding ipc it's waiting.
           *
           * Now synchronize sortlink preocedure is used, therefore the whole task scan needs
           * to be protected, preventing another core from doing sortlink deletion at same time.
           */
          LOS_SpinLock(&g_taskSpin);
      
          if (LOS_ListEmpty(listObject)) {
              LOS_SpinUnlock(&g_taskSpin);
              return;
          }
          sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);//拿本次Tick对应链表的SortLinkList的第一个节点sortLinkNode
          ROLLNUM_DEC(sortList->idxRollNum);//滚动数--
      
          while (ROLLNUM(sortList->idxRollNum) == 0) {//找到时间到了节点,注意这些节点都是由定时器产生的,
              LOS_ListDelete(&sortList->sortLinkNode);
              taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList);//拿任务,这里的任务都是超时任务
              taskCB->taskStatus &= ~OS_TASK_STATUS_PEND_TIME;
              tempStatus = taskCB->taskStatus;
              if (tempStatus & OS_TASK_STATUS_PEND) {
                  taskCB->taskStatus &= ~OS_TASK_STATUS_PEND;
      #if (LOSCFG_KERNEL_LITEIPC == YES)
                  taskCB->ipcStatus &= ~IPC_THREAD_STATUS_PEND;
      #endif
                  taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT;
                  LOS_ListDelete(&taskCB->pendList);
                  taskCB->taskSem = NULL;
                  taskCB->taskMux = NULL;
              } else {
                  taskCB->taskStatus &= ~OS_TASK_STATUS_DELAY;
              }
      
              if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
                  OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, OS_PROCESS_STATUS_PEND);
                  needSchedule = TRUE;
              }
      
              if (LOS_ListEmpty(listObject)) {
                  break;
              }
      
              sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
          }
      
          LOS_SpinUnlock(&g_taskSpin);
      
          if (needSchedule != FALSE) {//需要调度
              LOS_MpSchedule(OS_MP_CPU_ALL);//核间通讯,给所有CPU发送调度信号
              LOS_Schedule();//开始调度
          }
      }

      第三:定时器扫描,看是否有超时的定时器

      /*
       * Description: Tick interrupt interface module of software timer
       * Return     : LOS_OK on success or error code on failure
       *///OsSwtmrScan 由系统时钟中断处理函数调用
      LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID)//扫描定时器,如果碰到超时的,就放入超时队列
      {
          SortLinkList *sortList = NULL;
          SWTMR_CTRL_S *swtmr = NULL;
          SwtmrHandlerItemPtr swtmrHandler = NULL;
          LOS_DL_LIST *listObject = NULL;
          SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink;//拿到当前CPU的定时器链表
      
          swtmrSortLink->cursor = (swtmrSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
          listObject = swtmrSortLink->sortLink + swtmrSortLink->cursor;
       //由于swtmr是在特定的sortlink中,所以需要很小心的处理它,但其他CPU Core仍然有机会处理它,比如停止计时器
          /*
           * it needs to be carefully coped with, since the swtmr is in specific sortlink
           * while other cores still has the chance to process it, like stop the timer.
           */
          LOS_SpinLock(&g_swtmrSpin);
      
          if (LOS_ListEmpty(listObject)) {
              LOS_SpinUnlock(&g_swtmrSpin);
              return;
          }
          sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
          ROLLNUM_DEC(sortList->idxRollNum);
      
          while (ROLLNUM(sortList->idxRollNum) == 0) {
              sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
              LOS_ListDelete(&sortList->sortLinkNode);
              swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
      
              swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemboxAlloc(g_swtmrHandlerPool);//取出一个可用的软时钟处理项
              if (swtmrHandler != NULL) {
                  swtmrHandler->handler = swtmr->pfnHandler;
                  swtmrHandler->arg = swtmr->uwArg;
      
                  if (LOS_QueueWrite(OsPercpuGet()->swtmrHandlerQueue, swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) {
                      (VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandler);
                  }
              }
      
              if (swtmr->ucMode == LOS_SWTMR_MODE_ONCE) {
                  OsSwtmrDelete(swtmr);
      
                  if (swtmr->usTimerID < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) {
                      swtmr->usTimerID += LOSCFG_BASE_CORE_SWTMR_LIMIT;
                  } else {
                      swtmr->usTimerID %= LOSCFG_BASE_CORE_SWTMR_LIMIT;
                  }
              } else if (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE) {
                  swtmr->ucState = OS_SWTMR_STATUS_CREATED;
              } else {
                  swtmr->ucOverrun++;
                  OsSwtmrStart(swtmr);
              }
      
              if (LOS_ListEmpty(listObject)) {
                  break;
              }
      
              sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
          }
      
          LOS_SpinUnlock(&g_swtmrSpin);
      }

      最后看调度算法的实现

      //调度算法的实现
      VOID OsSchedResched(VOID)
      {
          LosTaskCB *runTask = NULL;
          LosTaskCB *newTask = NULL;
          LosProcessCB *runProcess = NULL;
          LosProcessCB *newProcess = NULL;
          LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));//必须持有任务自旋锁,自旋锁是不是进程层面去抢锁,而是CPU各自核之间去争夺锁
      
          if (!OsPreemptableInSched()) {//是否置了重新调度标识位
              return;
          }
          runTask = OsCurrTaskGet();//获取当前任务
          newTask = OsGetTopTask();//获取优先级最最最高的任务
          /* always be able to get one task */
          LOS_ASSERT(newTask != NULL);//不能没有需调度的任务
          if (runTask == newTask) {//当前任务就是最高任务,那还调度个啥的,直接退出.
              return;
          }
          runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;//当前任务状态位置成不在运行状态
          newTask->taskStatus |= OS_TASK_STATUS_RUNNING;//最高任务状态位置成在运行状态
          runProcess = OS_PCB_FROM_PID(runTask->processID);//通过进程ID索引拿到进程实体
          newProcess = OS_PCB_FROM_PID(newTask->processID);//同上
          OsSchedSwitchProcess(runProcess, newProcess);//切换进程,里面主要涉及进程空间的切换,也就是MMU的上下文切换.
      #if (LOSCFG_KERNEL_SMP == YES)//CPU多核的情况
          /* mask new running task's owner processor */
          runTask->currCpu = OS_TASK_INVALID_CPUID;//当前任务不占用CPU
          newTask->currCpu = ArchCurrCpuid();//让新任务占用CPU
      #endif
          (VOID)OsTaskSwitchCheck(runTask, newTask);//切换task的检查
      #if (LOSCFG_KERNEL_SCHED_STATISTICS == YES)
          OsSchedStatistics(runTask, newTask);
      #endif
          if ((newTask->timeSlice == 0) && (newTask->policy == LOS_SCHED_RR)) {//没有时间片且是抢占式调度的方式,注意 非抢占式都不需要时间片的.
              newTask->timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT;//给新任务时间片 默认 20ms
          }
          OsCurrTaskSet((VOID*)newTask);//设置新的task为CPU核的当前任务
          if (OsProcessIsUserMode(newProcess)) {//用户模式下会怎么样?
              OsCurrUserTaskSet(newTask->userArea);//设置task栈空间
          }
          /* do the task context switch */
          OsTaskSchedule(newTask, runTask);//切换任务上下文,注意OsTaskSchedule是一个汇编函数 见于 los_dispatch.s
      }

      编辑:hfy

      ?

      本期亮点

      发表评论

      elecfans网友

      分享到:

      用户评论(0

      精品文章

      更多>>

      推荐阅读

      每月人物

      模拟大咖对话:国产替代之后的中国模拟芯出路何在?

      模拟大咖对话:国产替代之后的中国模拟芯出路何在?
      (电子发烧友网报道 文/章鹰)9月中旬以来,电子时报报道,业内人士透露,随着交付周期延长到6个月以上,模拟芯片供应商德州仪器(TI)和安森美均已...

      思远的十年:从创业之初两个人,做到TWS耳机市场电源芯片市占第一

      思远的十年:从创业之初两个人,做到TWS耳机市场电源芯片市占
      8月6日,思远半导体举行十周年庆典,算是给过去十年画上完满的句号,并开启新的十年。在成立之初,公司只有两个人,到现在成为电源芯片的领先者,...

      每周排行

      • 型 号
      • 产品描述

      创新实用技术专题

      资料下载

      精品文章

      久久这里只有精品,在线视频 国产 日韩 欧美,国产精品人妻在线观看,亚洲中文欧美在线视频 磐安县| 海宁市| 冕宁县| 武安市| 榆林市| 小金县| 九江县| 五台县| 克拉玛依市| 天镇县| 从化市| 永宁县| 逊克县| 上思县| 云梦县| 城口县| 区。| 霍林郭勒市| 武夷山市| 新乡县| 阿拉善右旗| 革吉县| 东明县| 昌平区| 青阳县| 桑日县| 静安区| 封开县| 盐津县| 双柏县| 会泽县| 潼关县| 贵溪市| 酉阳| 洛扎县| 固阳县| 陕西省| 九龙城区| 怀化市| 丁青县| 陕西省| http://444 http://444 http://444 http://444 http://444 http://444