电竞比分网-中国电竞赛事及体育赛事平台

分享

鴻蒙內(nèi)核源碼分析(線程概念篇) | 線程,線程,它到底長(zhǎng)什么樣?

 月亮山的豬 2021-01-23

在鴻蒙內(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ì)清晰很多!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多