|
在鴻蒙內(nèi)核線程(thread)就是任務(wù)(task),也可以叫作業(yè).線程是對(duì)外的說(shuō)法,對(duì)內(nèi)就叫任務(wù).跟王二毛一樣, 在公司叫你王董,回到家里還有領(lǐng)導(dǎo),就叫二毛啊.這多親切.在鴻蒙內(nèi)核是大量的task,很少看到thread,只出現(xiàn)在posix層.當(dāng)一個(gè)東西理解就行. 任務(wù)(LosTaskCB)原始真身如下,本篇一一剖析它,看看它的五臟六腑里到底長(zhǎng)的是個(gè)啥. typedef struct { VOID *stackPointer; /**< Task stack pointer */ //非用戶模式下的棧指針 UINT16 taskStatus; /**< Task status */ //各種狀態(tài)標(biāo)簽,可以擁有多種標(biāo)簽,按位標(biāo)識(shí) UINT16 priority; /**< Task priority */ //任務(wù)優(yōu)先級(jí)站長(zhǎng)交易[0:31],默認(rèn)是31級(jí) UINT16 policy; //任務(wù)的調(diào)度方式(三種 .. LOS_SCHED_RR ) UINT16 timeSlice; /**< Remaining time slice *///剩余時(shí)間片 UINT32 stackSize; /**< Task stack size */ //非用戶模式下棧大小 UINTPTR topOfStack; /**< Task stack top */ //非用戶模式下的棧頂 bottom = top + size UINT32 taskID; /**< Task ID */ //任務(wù)ID,任務(wù)池本質(zhì)是一個(gè)大數(shù)組,ID就是數(shù)組的索引,默認(rèn) < 128 TSK_ENTRY_FUNC taskEntry; /**< Task entrance function */ //任務(wù)執(zhí)行入口函數(shù) VOID *joinRetval; /**< pthread adaption */ //用來(lái)存儲(chǔ)join線程的返回值 VOID *taskSem; /**< Task-held semaphore */ //task在等哪個(gè)信號(hào)量 VOID *taskMux; /**< Task-held mutex */ //task在等哪把鎖 VOID *taskEvent; /**< Task-held event */ //task在等哪個(gè)事件 UINTPTR args[4]; /**< Parameter, of which the maximum number is 4 */ //入口函數(shù)的參數(shù) 例如 main (int argc,char *argv[]) CHAR taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任務(wù)的名稱(chēng) LOS_DL_LIST pendList; /**< Task pend node */ //如果任務(wù)阻塞時(shí)就通過(guò)它掛到各種阻塞情況的鏈表上,比如OsTaskWait時(shí) LOS_DL_LIST threadList; /**< thread list */ //掛到所屬進(jìn)程的線程鏈表上 SortLinkList sortList; /**< Task sortlink node */ //掛到cpu core 的任務(wù)執(zhí)行鏈表上 UINT32 eventMask; /**< Event mask */ //事件屏蔽 UINT32 eventMode; /**< Event mode */ //事件模式 UINT32 priBitMap; /**< BitMap for recording the change of task priority, //任務(wù)在執(zhí)行過(guò)程中優(yōu)先級(jí)會(huì)經(jīng)常變化,這個(gè)變量用來(lái)記錄所有曾經(jīng)變化 the priority can not be greater than 31 */ //過(guò)的優(yōu)先級(jí),例如 ..01001011 曾經(jīng)有過(guò) 0,1,3,6 優(yōu)先級(jí) INT32 errorNo; /**< Error Num */ UINT32 signal; /**< Task signal */ //任務(wù)信號(hào)類(lèi)型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI) sig_cb sig; //信號(hào)控制塊,這里用于進(jìn)程間通訊的信號(hào),類(lèi)似于 linux singal模塊#if (LOSCFG_KERNEL_SMP == YES) UINT16 currCpu; /**< CPU core number of this task is running on */ //正在運(yùn)行此任務(wù)的CPU內(nèi)核號(hào) UINT16 lastCpu; /**< CPU core number of this task is running on last time */ //上次運(yùn)行此任務(wù)的CPU內(nèi)核號(hào) UINT16 cpuAffiMask; /**< CPU affinity mask, support up to 16 cores */ //CPU親和力掩碼,最多支持16核,親和力很重要,多核情況下盡量一個(gè)任務(wù)在一個(gè)CPU核上運(yùn)行,提高效率 UINT32 timerCpu; /**< CPU core number of this task is delayed or pended */ //此任務(wù)的CPU內(nèi)核號(hào)被延遲或掛起#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES) UINT32 syncSignal; /**< Synchronization for signal handling */ //用于CPU之間 同步信號(hào)#endif#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死鎖檢測(cè)開(kāi)關(guān) LockDep lockDep;#endif#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //調(diào)度統(tǒng)計(jì)開(kāi)關(guān),顯然打開(kāi)這個(gè)開(kāi)關(guān)性能會(huì)受到影響,鴻蒙默認(rèn)是關(guān)閉的 SchedStat schedStat; /**< Schedule statistics */ //調(diào)度統(tǒng)計(jì)#endif#endif UINTPTR userArea; //使用區(qū)域,由運(yùn)行時(shí)劃定,根據(jù)運(yùn)行態(tài)不同而不同 UINTPTR userMapBase; //用戶模式下的棧底位置 UINT32 userMapSize; /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */ UINT32 processID; /**< Which belong process *///所屬進(jìn)程ID FutexNode futex; //實(shí)現(xiàn)快鎖功能 LOS_DL_LIST joinList; /**< join list */ //聯(lián)結(jié)鏈表,允許任務(wù)之間相互釋放彼此 LOS_DL_LIST lockList; /**< Hold the lock list */ //拿到了哪些鎖鏈表 UINT32 waitID; /**< Wait for the PID or GID of the child process */ //等待孩子的PID或GID進(jìn)程 UINT16 waitFlag; /**< The type of child process that is waiting, belonging to a group or parent, a specific child process, or any child process */#if (LOSCFG_KERNEL_LITEIPC == YES) UINT32 ipcStatus; //IPC狀態(tài) LOS_DL_LIST msgListHead; //消息隊(duì)列頭結(jié)點(diǎn),上面掛的都是任務(wù)要讀的消息 BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//訪問(wèn)圖,指的是task之間是否能訪問(wèn)的標(biāo)識(shí),LOSCFG_BASE_CORE_TSK_LIMIT 為任務(wù)池總數(shù)#endif } LosTaskCB; 結(jié)構(gòu)體還是比較復(fù)雜,雖一一都做了注解,但還是不夠清晰,沒(méi)有模塊化.這里把它分解成以下六大塊逐一分析: 第一大塊:多核CPU相關(guān)塊 #if (LOSCFG_KERNEL_SMP == YES) //多CPU核支持 UINT16 currCpu; /**< CPU core number of this task is running on */ //正在運(yùn)行此任務(wù)的CPU內(nèi)核號(hào) UINT16 lastCpu; /**< CPU core number of this task is running on last time */ //上次運(yùn)行此任務(wù)的CPU內(nèi)核號(hào) UINT16 cpuAffiMask; /**< CPU affinity mask, support up to 16 cores */ //CPU親和力掩碼,最多支持16核,親和力很重要,多核情況下盡量一個(gè)任務(wù)在一個(gè)CPU核上運(yùn)行,提高效率 UINT32 timerCpu; /**< CPU core number of this task is delayed or pended */ //此任務(wù)的CPU內(nèi)核號(hào)被延遲或掛起#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES) UINT32 syncSignal; /**< Synchronization for signal handling */ //用于CPU之間 同步信號(hào)#endif#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死鎖檢測(cè)開(kāi)關(guān) LockDep lockDep;#endif#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //調(diào)度統(tǒng)計(jì)開(kāi)關(guān),顯然打開(kāi)這個(gè)開(kāi)關(guān)性能會(huì)受到影響,鴻蒙默認(rèn)是關(guān)閉的 SchedStat schedStat; /**< Schedule statistics */ //調(diào)度統(tǒng)計(jì)#endif#endif 鴻蒙內(nèi)核支持多CPU,誰(shuí)都知道多CPU當(dāng)然好,效率高,快嘛,但凡事有兩面性,在享受一個(gè)東西帶來(lái)好處的同時(shí),也得承擔(dān)伴隨它一起帶來(lái)的麻煩和風(fēng)險(xiǎn).多核有哪些的好處和麻煩,這里不展開(kāi)說(shuō),后續(xù)有專(zhuān)門(mén)的文章和視頻說(shuō)明.任務(wù)可叫線程,或叫作業(yè).CPU就是做作業(yè)的,多個(gè)CPU就是有多個(gè)能做作業(yè)的,一個(gè)作業(yè)能一鼓作氣做完嗎? 答案是:往往不行,因?yàn)楝F(xiàn)實(shí)不允許,作業(yè)可以有N多,而CPU數(shù)量非常有限,所以經(jīng)常做著A作業(yè)被老板打斷讓去做B作業(yè).這老板就是調(diào)度算法.A作業(yè)被打斷回來(lái)接著做的還會(huì)是原來(lái)那個(gè)CPU嗎? 答案是:不一定. 變量cpuAffiMask叫CPU親和力,它的作用是可以指定A的作業(yè)始終是同一個(gè)CPU來(lái)完成, 也可以隨便,交給調(diào)度算法,分到誰(shuí)就誰(shuí)來(lái),這方面可以不挑. 第二大塊:棧空間 VOID *stackPointer; /**< Task stack pointer */ //非用戶模式下的棧指針 UINT32 stackSize; /**< Task stack size */ //非用戶模式下棧大小 UINTPTR topOfStack; /**< Task stack top */ //非用戶模式下的棧頂 bottom = top + size UINTPTR userArea; //使用區(qū)域,由運(yùn)行時(shí)劃定,根據(jù)運(yùn)行態(tài)不同而不同 UINTPTR userMapBase; //用戶模式下的棧底位置 UINT32 userMapSize; /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */ 關(guān)于??臻g看過(guò)很多文章說(shuō)每個(gè)任務(wù)都有自己獨(dú)立的用戶??臻g和內(nèi)核??臻g,其實(shí)這種說(shuō)法不嚴(yán)謹(jǐn),至少在鴻蒙內(nèi)核不能這么去理解,否則會(huì)有很多疑惑,解釋不通的.CPU做作業(yè)就需要場(chǎng)地,棧空間就是給CPU做作業(yè)的場(chǎng)地.公司(內(nèi)核設(shè)計(jì)者)規(guī)定用戶的作業(yè)由用戶提供場(chǎng)地, 用戶場(chǎng)地就叫用戶??臻g,但有些作業(yè)比較敏感不方便在用戶現(xiàn)場(chǎng)完成,需要回到公司指定場(chǎng)地做,公司的場(chǎng)地就叫內(nèi)核棧空間.所以準(zhǔn)確的說(shuō)法是每個(gè)任務(wù)都有自己獨(dú)立的用戶??臻g和共用一個(gè)內(nèi)核棧空間.每個(gè)CPU在公司都有自己獨(dú)立的辦公位,不可能每個(gè)用戶作業(yè)在公司內(nèi)部有對(duì)應(yīng)辦公位的,明顯的浪費(fèi)內(nèi)核資源,行不通的.而那些敏感作業(yè)就叫系統(tǒng)調(diào)用. 第三大塊:資源競(jìng)爭(zhēng)/同步 VOID *taskSem; /**< Task-held semaphore */ //task在等哪個(gè)信號(hào)量 VOID *taskMux; /**< Task-held mutex */ //task在等哪把鎖 VOID *taskEvent; /**< Task-held event */ //task在等哪個(gè)事件 UINT32 eventMask; /**< Event mask */ //事件屏蔽 UINT32 eventMode; /**< Event mode */ //事件模式 FutexNode futex; //實(shí)現(xiàn)快鎖功能 LOS_DL_LIST joinList; /**< join list */ //聯(lián)結(jié)鏈表,允許任務(wù)之間相互釋放彼此 LOS_DL_LIST lockList; /**< Hold the lock list */ //拿到了哪些鎖鏈表 UINT32 signal; /**< Task signal */ //任務(wù)信號(hào)類(lèi)型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI) sig_cb sig; 公司的資源是有限的,CPU自己也是公司的資源,除了它還有其他的設(shè)備,比如做作業(yè)用的黑板,用戶A,B,C都可能用到,狼多肉少,咋搞? 互斥量(taskMux,futex)能解決這個(gè)問(wèn)題,辦事前先拿鎖,拿到了鎖的爽了,沒(méi)有拿到的就需要排隊(duì),在lockList上排,注意lockList是個(gè)雙向鏈表,它是內(nèi)核最重要的結(jié)構(gòu)體,開(kāi)篇就提過(guò),上面掛都是等鎖進(jìn)房間的西門(mén)大官人.這是互斥量的原理,解決任務(wù)間資源緊張的競(jìng)爭(zhēng)性問(wèn)題. 另外一個(gè)是用于任務(wù)的同步的信號(hào)量(sig_cb),任務(wù)和任務(wù)之間是會(huì)有關(guān)聯(lián)的,現(xiàn)實(shí)生活中公司的A,B用戶之間本身有業(yè)務(wù)往來(lái)的正常,CPU在幫B做作業(yè)的時(shí)候發(fā)現(xiàn)前置條件是需要A完成某項(xiàng)作業(yè)才能進(jìn)行,這時(shí)B就需要主動(dòng)讓出CPU先辦完A的事.這就是信號(hào)量的原理,解決的是任務(wù)間的同步問(wèn)題. 第四大塊:任務(wù)調(diào)度 前面說(shuō)過(guò)了作業(yè)N多,做作業(yè)的只有幾個(gè)人,單核CPU等于只有一個(gè)人干活.那要怎么分配CPU,就需要調(diào)度算法. UINT16 taskStatus; /**< Task status */ //各種狀態(tài)標(biāo)簽,可以擁有多種標(biāo)簽,按位標(biāo)識(shí) UINT16 priority; /**< Task priority */ //任務(wù)優(yōu)先級(jí)[0:31],默認(rèn)是31級(jí) UINT16 policy; //任務(wù)的調(diào)度方式(三種 .. LOS_SCHED_RR ) UINT16 timeSlice; /**< Remaining time slice *///剩余時(shí)間片 CHAR taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任務(wù)的名稱(chēng) LOS_DL_LIST pendList; /**< Task pend node */ //如果任務(wù)阻塞時(shí)就通過(guò)它掛到各種阻塞情況的鏈表上,比如OsTaskWait時(shí) LOS_DL_LIST threadList; /**< thread list */ //掛到所屬進(jìn)程的線程鏈表上 SortLinkList sortList; /**< Task sortlink node */ //掛到cpu core 的任務(wù)執(zhí)行鏈表上 是簡(jiǎn)單的先來(lái)后到(FIFO)嗎? 當(dāng)然也支持這個(gè)方式.鴻蒙內(nèi)核用的是搶占式調(diào)度(policy),就是可以插隊(duì),比優(yōu)先級(jí)(priority)大小,[0,31]級(jí),數(shù)字越大的優(yōu)先級(jí)越低,跟考試一樣,排第一才是最牛的,鴻蒙排0的最牛! 想也想得到內(nèi)核的任務(wù)優(yōu)先級(jí)都是很高的,比如資源回收任務(wù)排第5,定時(shí)器任務(wù)排第0.夠牛了吧.普通老百姓排多少呢?默認(rèn)28級(jí),慘!!! 另外任務(wù)有時(shí)間限制timeSlice,叫時(shí)間片,默認(rèn)20ms,用完了會(huì)給你重置,發(fā)起重新調(diào)度,找出優(yōu)先級(jí)高的執(zhí)行,阻塞的任務(wù)(比如沒(méi)拿到鎖的,等信號(hào)量同步的,等讀寫(xiě)消息隊(duì)列的)都掛到pendList上,方便管理. 第五大塊:任務(wù)間通訊 #if (LOSCFG_KERNEL_LITEIPC == YES) UINT32 ipcStatus; //IPC狀態(tài) LOS_DL_LIST msgListHead; //消息隊(duì)列頭結(jié)點(diǎn),上面掛的都是任務(wù)要讀的消息 BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//訪問(wèn)圖,指的是task之間是否能訪問(wèn)的標(biāo)識(shí),LOSCFG_BASE_CORE_TSK_LIMIT 為任務(wù)池總數(shù)#endif 這個(gè)很重要,解決任務(wù)間通訊問(wèn)題,要知道進(jìn)程負(fù)責(zé)的是資源的管理功能,什么意思?就是它不并負(fù)責(zé)內(nèi)容的生產(chǎn)和消費(fèi),它只負(fù)責(zé)管理確保你的內(nèi)容到達(dá)率和完整性.生產(chǎn)者和消費(fèi)者始終是任務(wù).進(jìn)程管了哪些東西系列篇有專(zhuān)門(mén)的文章,自行翻看.liteipc是鴻蒙專(zhuān)有的通訊消息隊(duì)列實(shí)現(xiàn).簡(jiǎn)單說(shuō)它是基于文件的,而傳統(tǒng)的ipc消息隊(duì)列是基于內(nèi)存的.有什么區(qū)別也不在這里討論,已有專(zhuān)門(mén)的文章分析. 第六大塊:輔助工具 要知道任務(wù)對(duì)內(nèi)核來(lái)說(shuō)太重要了,是任務(wù)讓CPU忙里忙外的,那中間出差錯(cuò)了怎么辦,怎么診斷你問(wèn)題出哪里了,就需要一些工具,比如死鎖檢測(cè),比如占用CPU,內(nèi)存監(jiān)控 如下: #if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死鎖檢測(cè)開(kāi)關(guān) LockDep lockDep;#endif#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //調(diào)度統(tǒng)計(jì)開(kāi)關(guān),顯然打開(kāi)這個(gè)開(kāi)關(guān)性能會(huì)受到影響,鴻蒙默認(rèn)是關(guān)閉的 SchedStat schedStat; /**< Schedule statistics */ //調(diào)度統(tǒng)計(jì)#endif 以上就是任務(wù)的五臟六腑,看清楚它鴻蒙內(nèi)核的影像會(huì)清晰很多! |
|
|