 最近有人在Github上開源了鴻蒙OS(https://www.github.com/Awesome-HarmonyOS)并且累計(jì)獲得了一萬多顆Star。從華為的官方宣傳中就提到了“安卓總代碼超過一億行,其中內(nèi)核代碼超過2000萬行,實(shí)際用到的不過8%,如此龐大和冗余的這種設(shè)計(jì),實(shí)際上很難保證流暢度,使用效率很低。” 而筆者之前介紹過的TDengine(https://github.com/taosdata/TDengine)做為一個(gè)數(shù)據(jù)庫項(xiàng)目更是僅用1.5M安裝包就能搞定,代碼效率高的驚人。所以從這方面我們也能看出優(yōu)秀的項(xiàng)目對于速度的要求都是極致的。不過這兩個(gè)項(xiàng)目開源后都引發(fā)了一些爭議,比如鴻蒙開源當(dāng)天就有人發(fā)微博說華為只是做了個(gè)安卓的定制版,質(zhì)量甚至還不如MIUI,筆者的這位創(chuàng)造Github冠軍項(xiàng)目的老男人,堪稱10倍程序員本尊發(fā)布后,也有人在評論說TDengine的consumer-productor實(shí)現(xiàn)無法通過code review。但是仔細(xì)閱讀這些評論可以發(fā)現(xiàn),這些批評其實(shí)都不是基于代碼的。筆者做為一名程序員奉行“Talk is cheap,show me the code'的理念,所以我利用周末時(shí)間閱讀了這兩個(gè)項(xiàng)目的代碼,發(fā)現(xiàn)了很多值得學(xué)習(xí)的設(shè)計(jì)亮點(diǎn)。尤其是鴻蒙OS做為操作系統(tǒng)項(xiàng)目而Tdengine做為數(shù)據(jù)庫項(xiàng)目,比較他們兩者在同一模塊上的設(shè)計(jì)異同,非常有收獲,下面給各位讀者分享一下,如有意見歡迎留言。兩個(gè)項(xiàng)目對于任務(wù)調(diào)度模塊的實(shí)現(xiàn)對比與一般操作系統(tǒng)一樣,鴻蒙也將任務(wù)狀態(tài)通常分為以下三種:就緒(Ready): 該任務(wù)在就緒列表中,只等待CPU。
運(yùn)行(Running): 該任務(wù)正在執(zhí)行。 其代碼位置在los_task.c,以任務(wù)恢復(fù)函數(shù)LOS_TaskResume為例,其代碼如下:LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskResume(UINT32 uwTaskID) { UINTPTR uvIntSave; LOS_TASK_CB *pstTaskCB; UINT16 usTempStatus; UINT32 uwErrRet = OS_ERROR;
if (uwTaskID > LOSCFG_BASE_CORE_TSK_LIMIT) { return LOS_ERRNO_TSK_ID_INVALID; }
pstTaskCB = OS_TCB_FROM_TID(uwTaskID); uvIntSave = LOS_IntLock(); usTempStatus = pstTaskCB->usTaskStatus;
if (OS_TASK_STATUS_UNUSED & usTempStatus) { uwErrRet = LOS_ERRNO_TSK_NOT_CREATED; OS_GOTO_ERREND(); } else if (!(OS_TASK_STATUS_SUSPEND & usTempStatus)) { uwErrRet = LOS_ERRNO_TSK_NOT_SUSPENDED; OS_GOTO_ERREND(); } //以上為任務(wù)狀態(tài)檢查 pstTaskCB->usTaskStatus &= (~OS_TASK_STATUS_SUSPEND);//清除任務(wù)的suspend標(biāo)志位置 if (!(OS_CHECK_TASK_BLOCK & pstTaskCB->usTaskStatus) )//若任務(wù)的還自在阻塞狀態(tài)則變?yōu)榫途w狀態(tài) ,并調(diào)用 LOS_Schedule()進(jìn)行調(diào)度 { pstTaskCB->usTaskStatus |= OS_TASK_STATUS_READY; LOS_PriqueueEnqueue(&pstTaskCB->stPendList, pstTaskCB->usPriority); if (g_bTaskScheduled) { (VOID)LOS_IntRestore(uvIntSave); LOS_Schedule(); return LOS_OK; } g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(LOS_PriqueueTop(), LOS_TASK_CB, stPendList); /*lint !e413*/ }
(VOID)LOS_IntRestore(uvIntSave); return LOS_OK;
LOS_ERREND: (VOID)LOS_IntRestore(uvIntSave); return uwErrRet; }
我們看到這個(gè)函數(shù)的處理過程基本分為三步:我們知道完整的LINUX內(nèi)核是支持將任務(wù)指定在某個(gè)CPU上運(yùn)行的,不過鴻蒙OS做為一個(gè)微內(nèi)核的移動(dòng)操作系統(tǒng)沒有繼承這些復(fù)雜的功能,直接做了減法,實(shí)現(xiàn)一個(gè)最簡模型。2.TdEngine的任務(wù)調(diào)度模塊 而對比TDengine的調(diào)度模塊tsched.c,可以看到TDengine更是放棄了任務(wù)優(yōu)先級調(diào)度功能,因?yàn)樽鰹闀r(shí)序數(shù)據(jù)庫其數(shù)據(jù)全是按照生成時(shí)間排序處理入庫的,所以他的只將任務(wù)調(diào)度模塊,僅實(shí)現(xiàn)了以下四個(gè)功能從其循環(huán)處理任務(wù)的函數(shù)(taosProcessSchedQueue),可以看出它只是隊(duì)尾不斷取出任務(wù)進(jìn)行循環(huán)處理,而沒有優(yōu)化級調(diào)整排序的過程。void *taosProcessSchedQueue(void *param) { SSchedMsg msg; SSchedQueue *pSched = (SSchedQueue *)param;
while (1) { if (sem_wait(&pSched->fullSem) != 0) { pError('wait %s fullSem failed, errno:%d, reason:%s', pSched->label, errno, strerror(errno)); if (errno == EINTR) { /* sem_wait is interrupted by interrupt, ignore and continue */ continue; } }
if (pthread_mutex_lock(&pSched->queueMutex) != 0) pError('lock %s queueMutex failed, reason:%s', pSched->label, strerror(errno));
msg = pSched->queue[pSched->fullSlot]; memset(pSched->queue pSched->fullSlot, 0, sizeof(SSchedMsg)); pSched->fullSlot = (pSched->fullSlot 1) % pSched->queueSize;//從隊(duì)尾取出消息不斷處理
if (pthread_mutex_unlock(&pSched->queueMutex) != 0) pError('unlock %s queueMutex failed, reason:%s ', pSched->label, strerror(errno));
if (sem_post(&pSched->emptySem) != 0) pError('post %s emptySem failed, reason:%s ', pSched->label, strerror(errno));
if (msg.fp) (*(msg.fp))(&msg); else if (msg.tfp) (*(msg.tfp))(msg.ahandle, msg.thandle); } }
int taosScheduleTask(void *qhandle, SSchedMsg *pMsg) { SSchedQueue *pSched = (SSchedQueue *)qhandle; if (pSched == NULL) { pError('sched is not ready, msg:%p is dropped', pMsg); return 0; }
if (sem_wait(&pSched->emptySem) != 0) pError('wait %s emptySem failed, reason:%s', pSched->label, strerror(errno));
if (pthread_mutex_lock(&pSched->queueMutex) != 0) pError('lock %s queueMutex failed, reason:%s', pSched->label, strerror(errno));
pSched->queue[pSched->emptySlot] = *pMsg; pSched->emptySlot = (pSched->emptySlot 1) % pSched->queueSize;
if (pthread_mutex_unlock(&pSched->queueMutex) != 0) pError('unlock %s queueMutex failed, reason:%s', pSched->label, strerror(errno));
if (sem_post(&pSched->fullSem) != 0) pError('post %s fullSem failed, reason:%s', pSched->label, strerror(errno));
return 0; }
兩個(gè)項(xiàng)目對于定時(shí)器(timer)的實(shí)現(xiàn)對比軟件定時(shí)器,是基于系統(tǒng)Tick時(shí)鐘中斷且由軟件來模擬的定時(shí)器,當(dāng)經(jīng)過設(shè)定的Tick時(shí)鐘計(jì)數(shù)值后會(huì)觸發(fā)用戶定義的回調(diào)函數(shù)。定時(shí)精度與系統(tǒng)Tick時(shí)鐘的周期有關(guān)。 硬件定時(shí)器受硬件的限制,數(shù)量上不足以滿足用戶的實(shí)際需求,因此為了滿足用戶需求,提供更多的定時(shí)器,Huawei LiteOS操作系統(tǒng)提供軟件定時(shí)器功能。軟件定時(shí)器擴(kuò)展了定時(shí)器的數(shù)量,允許創(chuàng)建更多的定時(shí)業(yè)務(wù)。如果官方文檔的說明沒看懂,可以直接查閱其源代碼,具體位置在los_swtmr.c下面筆者來簡述一下鴻蒙定時(shí)器的工作原理。LITE_OS_SEC_TEXT VOID osSwTmrTask(VOID) { SWTMR_HANDLER_ITEM_P pstSwtmrHandle = (SWTMR_HANDLER_ITEM_P)NULL; SWTMR_HANDLER_ITEM_S stSwtmrHandle; UINT32 uwRet;
for ( ; ; ) { uwRet = LOS_QueueRead(m_uwSwTmrHandlerQueue, &pstSwtmrHandle, sizeof(SWTMR_HANDLER_ITEM_P), LOS_WAIT_FOREVER); if (uwRet == LOS_OK) { if (pstSwtmrHandle != NULL) { stSwtmrHandle.pfnHandler = pstSwtmrHandle->pfnHandler; stSwtmrHandle.uwArg = pstSwtmrHandle->uwArg; (VOID)LOS_MemboxFree(m_aucSwTmrHandlerPool, pstSwtmrHandle); if (stSwtmrHandle.pfnHandler != NULL) { stSwtmrHandle.pfnHandler(stSwtmrHandle.uwArg); } } } }//end of for }
以上函數(shù)的運(yùn)行原理動(dòng)畫解析如下: 其實(shí)Tdengine的timer我之前已經(jīng)做過解讀了,200行代碼為大家解讀這個(gè)Github冠軍項(xiàng)目背后的定時(shí)器。就不加贅述了,這里把鴻蒙和Tdengine的timer做一下簡單的對比:節(jié)約關(guān)鍵資源:由于每個(gè)操作系統(tǒng)的timer都需要一個(gè)線程進(jìn)行回調(diào)處理,這對于Tdengine這種數(shù)據(jù)庫動(dòng)轍幾萬個(gè)timer的應(yīng)用來說是不可接受的,所以為了節(jié)省線程資源,Td要用自己實(shí)現(xiàn)的timer之所以要實(shí)現(xiàn)自己的定時(shí)器是為了節(jié)省線程資源。而鴻蒙實(shí)現(xiàn)timer則是為了節(jié)約硬件定時(shí)器資源。最簡化設(shè)計(jì):兩個(gè)timer都沒有優(yōu)先級的設(shè)定。其中鴻蒙OS做為移動(dòng)操作系統(tǒng)直接將timer的精度值也舍棄了。使用雙鏈表提高效率:兩個(gè)timer都使用雙鏈表來存儲(chǔ)同一時(shí)刻到期的定時(shí)器,這樣能節(jié)省遍歷和移動(dòng)的時(shí)間,大大提高效率。從上面這兩個(gè)簡單的模塊中我們也看到這些優(yōu)秀的項(xiàng)目都使用最精簡的設(shè)計(jì),緊貼需求、甩掉包袱、輕裝上陣才能回歸本質(zhì)取得成功。無論是TdEngine取消任務(wù)調(diào)度的優(yōu)先級排序,還是鴻蒙放棄對定時(shí)器精度的支持,都是看來出乎意料,實(shí)則頗具內(nèi)涵的減法操作。真正優(yōu)秀的項(xiàng)目都是敢于做減法的,只有減掉那些看似高大上的設(shè)計(jì),才能向著有取有舍,大道至簡的境界邁進(jìn)。
|