時鐘概念
時間是非常重要的概念,我們整個學(xué)生階段有個東西很重要,就是校園鈴聲. 它控制著上課,下課,吃飯,睡覺的節(jié)奏.沒有它學(xué)校的管理就亂套了,老師拖課想拖多久就多久,那可不行,下課鈴聲一響就是在告訴老師時間到了,該停止了讓學(xué)生HAPPY去了.
操作系統(tǒng)也一樣,需要通過時間來規(guī)范其任務(wù)的執(zhí)行,操作系統(tǒng)中最小的時間單位是時鐘節(jié)拍 (OS Tick)。任何操作系統(tǒng)都需要提供一個時鐘節(jié)拍,以供系統(tǒng)處理所有和時間有關(guān)的事件,如線程的延時、線程的時間片輪轉(zhuǎn)調(diào)度以及定時器超時等。時鐘節(jié)拍是特定的周期性中斷,這個中斷可以看做是系統(tǒng)心跳,中斷之間的時間間隔取決于不同的應(yīng)用,一般是 1ms–100ms,時鐘節(jié)拍率越快,系統(tǒng)的實時響應(yīng)越快,但是系統(tǒng)的額外開銷就越大,從系統(tǒng)啟動開始計數(shù)的時鐘節(jié)拍數(shù)稱為系統(tǒng)時間。
在鴻蒙內(nèi)核中,時鐘節(jié)拍的長度可以根據(jù) LOSCFG_BASE_CORE_TICK_PER_SECOND 的定義來調(diào)整,等于 1/LOSCFG_BASE_CORE_TICK_PER_SECOND 秒。
時鐘節(jié)拍的實現(xiàn)方式
時鐘節(jié)拍由配置為中斷觸發(fā)模式的硬件定時器產(chǎn)生,當(dāng)中斷到來時,將調(diào)用一次:void OsTickHandler(void),通知操作系統(tǒng)已經(jīng)過去一個系統(tǒng)時鐘;不同硬件定時器中斷實現(xiàn)都不同,
/** * @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次觸發(fā),當(dāng)然這是可以改的 #endif
每秒100個tick,時間單位為10毫秒, 即每秒調(diào)用時鐘中斷處理程序100次.
/* * Description : Tick interruption handler *///節(jié)拍中斷處理函數(shù) ,鴻蒙默認10ms觸發(fā)一次 LITE_OS_SEC_TEXT VOID OsTickHandler(VOID) { //... OsTimesliceCheck();//進程和任務(wù)的時間片檢查 OsTaskScan(); /* task timeout scan *///任務(wù)掃描 #if (LOSCFG_BASE_CORE_SWTMR == YES) OsSwtmrScan();//定時器掃描,看是否有超時的定時器 #endif }
它主要干了三件事情
第一:檢查當(dāng)前任務(wù)的時間片,任務(wù)執(zhí)行一次分配多少時間呢?答案是2個時間片,即 20ms.
#ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT #define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2 //2個時間片,20ms #endif //檢查進程和任務(wù)的時間片,如果沒有時間片了直接調(diào)度 LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID) { LosTaskCB *runTask = NULL; LosProcessCB *runProcess = OsCurrProcessGet();//獲取當(dāng)前進程 if (runProcess->policy != LOS_SCHED_RR) {//進程調(diào)度算法是否是搶占式 goto SCHED_TASK;//進程不是搶占式調(diào)度直接去檢查任務(wù)的時間片 } if (runProcess->timeSlice != 0) {//進程還有時間片嗎? runProcess->timeSlice--;//進程時間片減少一次 if (runProcess->timeSlice == 0) {//沒有時間片了 LOS_Schedule();//進程時間片用完,發(fā)起調(diào)度 } } SCHED_TASK: runTask = OsCurrTaskGet();//獲取當(dāng)前任務(wù) if (runTask->policy != LOS_SCHED_RR) {//任務(wù)調(diào)度算法是否是搶占式 return;//任務(wù)不是搶占式調(diào)度直接結(jié)束檢查 } if (runTask->timeSlice != 0) {//任務(wù)還有時間片嗎? runTask->timeSlice--;//任務(wù)時間片也減少一次 if (runTask->timeSlice == 0) {//沒有時間片了 LOS_Schedule();//任務(wù)時間片用完,發(fā)起調(diào)度 } } }
第二:掃描任務(wù),主要是檢查被阻塞的任務(wù)是否可以被重新調(diào)度
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;//獲取任務(wù)的排序鏈表 taskSortLink->cursor = (taskSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK; listObject = taskSortLink->sortLink + taskSortLink->cursor;//只處理這個游標上的鏈表,因為系統(tǒng)對超時任務(wù)都已經(jīng)規(guī)鏈表了. //當(dāng)任務(wù)因超時而掛起時,任務(wù)塊處于超時排序鏈接上,(每個cpu)和ipc(互斥鎖、掃描電鏡等)的塊同時被喚醒 /*不管是超時還是相應(yīng)的ipc,它都在等待?,F(xiàn)在使用synchronize sortlink precedure,因此整個任務(wù)掃描需要保護,防止另一個核心同時刪除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對應(yīng)鏈表的SortLinkList的第一個節(jié)點sortLinkNode ROLLNUM_DEC(sortList->idxRollNum);//滾動數(shù)-- while (ROLLNUM(sortList->idxRollNum) == 0) {//找到時間到了節(jié)點,注意這些節(jié)點都是由定時器產(chǎn)生的, LOS_ListDelete(&sortList->sortLinkNode); taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList);//拿任務(wù),這里的任務(wù)都是超時任務(wù) 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) {//需要調(diào)度 LOS_MpSchedule(OS_MP_CPU_ALL);//核間通訊,給所有CPU發(fā)送調(diào)度信號 LOS_Schedule();//開始調(diào)度 } }
第三:定時器掃描,看是否有超時的定時器
/* * Description: Tick interrupt interface module of software timer * Return : LOS_OK on success or error code on failure *///OsSwtmrScan 由系統(tǒng)時鐘中斷處理函數(shù)調(diào)用 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;//拿到當(dāng)前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); }
最后看調(diào)度算法的實現(xiàn)
//調(diào)度算法的實現(xiàn) VOID OsSchedResched(VOID) { LosTaskCB *runTask = NULL; LosTaskCB *newTask = NULL; LosProcessCB *runProcess = NULL; LosProcessCB *newProcess = NULL; LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));//必須持有任務(wù)自旋鎖,自旋鎖是不是進程層面去搶鎖,而是CPU各自核之間去爭奪鎖 if (!OsPreemptableInSched()) {//是否置了重新調(diào)度標識位 return; } runTask = OsCurrTaskGet();//獲取當(dāng)前任務(wù) newTask = OsGetTopTask();//獲取優(yōu)先級最最最高的任務(wù) /* always be able to get one task */ LOS_ASSERT(newTask != NULL);//不能沒有需調(diào)度的任務(wù) if (runTask == newTask) {//當(dāng)前任務(wù)就是最高任務(wù),那還調(diào)度個啥的,直接退出. return; } runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;//當(dāng)前任務(wù)狀態(tài)位置成不在運行狀態(tài) newTask->taskStatus |= OS_TASK_STATUS_RUNNING;//最高任務(wù)狀態(tài)位置成在運行狀態(tài) 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;//當(dāng)前任務(wù)不占用CPU newTask->currCpu = ArchCurrCpuid();//讓新任務(wù)占用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)) {//沒有時間片且是搶占式調(diào)度的方式,注意 非搶占式都不需要時間片的. newTask->timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT;//給新任務(wù)時間片 默認 20ms } OsCurrTaskSet((VOID*)newTask);//設(shè)置新的task為CPU核的當(dāng)前任務(wù) if (OsProcessIsUserMode(newProcess)) {//用戶模式下會怎么樣? OsCurrUserTaskSet(newTask->userArea);//設(shè)置task??臻g } /* do the task context switch */ OsTaskSchedule(newTask, runTask);//切換任務(wù)上下文,注意OsTaskSchedule是一個匯編函數(shù) 見于 los_dispatch.s }
編輯:hfy
-
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6801瀏覽量
123282 -
定時器
+關(guān)注
關(guān)注
23文章
3246瀏覽量
114715 -
系統(tǒng)時鐘
+關(guān)注
關(guān)注
1文章
30瀏覽量
9306 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2634瀏覽量
66302
發(fā)布評論請先 登錄
相關(guān)推薦
評論