時鐘概念
時間是非常重要的概念,我們整個學生階段有個東西很重要,就是校園鈴聲. 它控制著上課,下課,吃飯,睡覺的節奏.沒有它學校的管理就亂套了,老師拖課想拖多久就多久,那可不行,下課鈴聲一響就是在告訴老師時間到了,該停止了讓學生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
-
操作系統
+關注
關注
37文章
6850瀏覽量
123432 -
定時器
+關注
關注
23文章
3251瀏覽量
115013 -
系統時鐘
+關注
關注
1文章
30瀏覽量
9328 -
鴻蒙系統
+關注
關注
183文章
2636瀏覽量
66449
發布評論請先 登錄
相關推薦
評論