任務(wù)即線程
在鴻蒙內(nèi)核中,廣義上可理解為一個(gè)任務(wù)就是一個(gè)線程
官方是怎么描述線程的
基本概念
從系統(tǒng)的角度看,線程是競(jìng)爭(zhēng)系統(tǒng)資源的最小運(yùn)行單元。線程可以使用或等待CPU、使用內(nèi)存空間等系統(tǒng)資源,并獨(dú)立于其它線程運(yùn)行。
鴻蒙內(nèi)核每個(gè)進(jìn)程內(nèi)的線程獨(dú)立運(yùn)行、獨(dú)立調(diào)度,當(dāng)前進(jìn)程內(nèi)線程的調(diào)度不受其它進(jìn)程內(nèi)線程的影響。
鴻蒙內(nèi)核中的線程采用搶占式調(diào)度機(jī)制,同時(shí)支持時(shí)間片輪轉(zhuǎn)調(diào)度和FIFO調(diào)度方式。
鴻蒙內(nèi)核的線程一共有32個(gè)優(yōu)先級(jí)(0-31),最高優(yōu)先級(jí)為0,最低優(yōu)先級(jí)為31。
當(dāng)前進(jìn)程內(nèi)高優(yōu)先級(jí)的線程可搶占當(dāng)前進(jìn)程內(nèi)低優(yōu)先級(jí)線程,當(dāng)前進(jìn)程內(nèi)低優(yōu)先級(jí)線程必須在當(dāng)前進(jìn)程內(nèi)高優(yōu)先級(jí)線程阻塞或結(jié)束后才能得到調(diào)度。
線程狀態(tài)說明:
初始化(Init):該線程正在被創(chuàng)建。
就緒(Ready):該線程在就緒列表中,等待CPU調(diào)度。
運(yùn)行(Running):該線程正在運(yùn)行。
阻塞(Blocked):該線程被阻塞掛起。Blocked狀態(tài)包括:pend(因?yàn)殒i、事件、信號(hào)量等阻塞)、suspend(主動(dòng)pend)、delay(延時(shí)阻塞)、pendtime(因?yàn)殒i、事件、信號(hào)量時(shí)間等超時(shí)等待)。
退出(Exit):該線程運(yùn)行結(jié)束,等待父線程回收其控制塊資源。
圖 1 線程狀態(tài)遷移示意圖
注意官方文檔說的是線程,沒有提到task(任務(wù)),但內(nèi)核源碼中卻有大量 task代碼,很少有線程(thread)代碼 ,這是怎么回事?
其實(shí)在鴻蒙內(nèi)核中, task就是線程, 初學(xué)者完全可以這么理解,但二者還是有區(qū)別,否則干嘛要分兩個(gè)詞描述。
會(huì)有什么區(qū)別?是管理上的區(qū)別,task是調(diào)度層面的概念,線程是進(jìn)程層面的概念。 就像同一個(gè)人在不同的管理體系中會(huì)有不同的身份一樣,一個(gè)男人既可以是 孩子,爸爸,丈夫,或者程序員,視角不同功能也會(huì)不同。
如何證明是一個(gè)東西,繼續(xù)再往下看。
執(zhí)行task命令
看shell task 命令的執(zhí)行結(jié)果:
task命令 查出每個(gè)任務(wù)在生命周期內(nèi)的運(yùn)行情況,它運(yùn)行的內(nèi)存空間,優(yōu)先級(jí),時(shí)間片,入口執(zhí)行函數(shù),進(jìn)程ID,狀態(tài)等等信息,非常的復(fù)雜。這么復(fù)雜的信息就需要一個(gè)結(jié)構(gòu)體來承載。而這個(gè)結(jié)構(gòu)體就是 LosTaskCB(任務(wù)控制塊)
對(duì)應(yīng)張大爺?shù)墓适拢簍ask就是一個(gè)用戶的節(jié)目清單里的一個(gè)節(jié)目,用戶總清單就是一個(gè)進(jìn)程,所以上面會(huì)有很多的節(jié)目。
task長(zhǎng)得什么樣子
說LosTaskCB之前先說下官方文檔任務(wù)狀態(tài)對(duì)應(yīng)的 define,可以看出task和線程是一個(gè)東西。
#define OS_TASK_STATUS_INIT 0x0001U #define OS_TASK_STATUS_READY 0x0002U #define OS_TASK_STATUS_RUNNING 0x0004U #define OS_TASK_STATUS_SUSPEND 0x0008U #define OS_TASK_STATUS_PEND 0x0010U #define OS_TASK_STATUS_DELAY 0x0020U #define OS_TASK_STATUS_TIMEOUT 0x0040U #define OS_TASK_STATUS_PEND_TIME 0x0080U #define OS_TASK_STATUS_EXIT 0x0100U
LosTaskCB長(zhǎng)什么樣?抱歉,它確實(shí)有點(diǎn)長(zhǎng),但還是要全部貼出全貌。
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í)[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 */ //用來存儲(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ù)的名稱 LOS_DL_LIST pendList; /**< Task pend node */ //如果任務(wù)阻塞時(shí)就通過它掛到各種阻塞情況的鏈表上,比如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í)行過程中優(yōu)先級(jí)會(huì)經(jīng)常變化,這個(gè)變量用來記錄所有曾經(jīng)變化 the priority can not be greater than 31 */ //過的優(yōu)先級(jí),例如 ..01001011 曾經(jīng)有過 0,1,3,6 優(yōu)先級(jí) INT32 errorNo; /**< Error Num */ UINT32 signal; /**< Task signal */ //任務(wù)信號(hào)類型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI) sig_cb sig; //信號(hào)控制塊,這里用于進(jìn)程間通訊的信號(hào),類似于 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è)開關(guān) LockDep lockDep; #endif #if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //調(diào)度統(tǒng)計(jì)開關(guān),顯然打開這個(gè)開關(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];//訪問圖,指的是task之間是否能訪問的標(biāo)識(shí),LOSCFG_BASE_CORE_TSK_LIMIT 為任務(wù)池總數(shù) #endif } LosTaskCB;
結(jié)構(gòu)體LosTaskCB內(nèi)容很多,各代表什么含義?
LosTaskCB相當(dāng)于任務(wù)在內(nèi)核中的身份證,它反映出每個(gè)任務(wù)在生命周期內(nèi)的運(yùn)行情況。既然是周期就會(huì)有狀態(tài),要運(yùn)行就需要內(nèi)存空間,就需要被內(nèi)核算法調(diào)度,被選中CPU就去執(zhí)行代碼段指令,CPU要執(zhí)行就需要告訴它從哪里開始執(zhí)行,因?yàn)槭嵌嗑€程,但只有一個(gè)CPU就需要不斷的切換任務(wù),那執(zhí)行會(huì)被中斷,也需要再恢復(fù)后繼續(xù)執(zhí)行,又如何保證恢復(fù)的任務(wù)執(zhí)行不會(huì)出錯(cuò),這些問題都需要說明白。
Task怎么管理
什么是任務(wù)池?
前面已經(jīng)說了任務(wù)是內(nèi)核調(diào)度層面的概念,調(diào)度算法保證了task有序的執(zhí)行,調(diào)度機(jī)制詳見其他姊妹篇的介紹。
如此多的任務(wù)怎么管理和執(zhí)行?管理靠任務(wù)池和就緒隊(duì)列,執(zhí)行靠調(diào)度算法。
代碼如下(OsTaskInit):
LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID) { UINT32 index; UINT32 ret; UINT32 size; g_taskMaxNum = LOSCFG_BASE_CORE_TSK_LIMIT;//任務(wù)池中最多默認(rèn)128個(gè),可謂鐵打的任務(wù)池流水的線程 size = (g_taskMaxNum + 1) * sizeof(LosTaskCB);//計(jì)算需分配內(nèi)存總大小 /* * This memory is resident memory and is used to save the system resources * of task control block and will not be freed. */ g_taskCBArray = (LosTaskCB *)LOS_MemAlloc(m_aucSysMem0, size);//任務(wù)池 常駐內(nèi)存,不被釋放 if (g_taskCBArray == NULL) { return LOS_ERRNO_TSK_NO_MEMORY; } (VOID)memset_s(g_taskCBArray, size, 0, size); LOS_ListInit(&g_losFreeTask);//空閑任務(wù)鏈表 LOS_ListInit(&g_taskRecyleList);//需回收任務(wù)鏈表 for (index = 0; index < g_taskMaxNum; index++) { g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED; g_taskCBArray[index].taskID = index;//任務(wù)ID最大默認(rèn)127 LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);//都插入空閑任務(wù)列表 }//注意:這里掛的是pendList節(jié)點(diǎn),所以取TCB要通過 OS_TCB_FROM_PENDLIST 取. ret = OsPriQueueInit();//創(chuàng)建32個(gè)任務(wù)優(yōu)先級(jí)隊(duì)列,即32個(gè)雙向循環(huán)鏈表 if (ret != LOS_OK) { return LOS_ERRNO_TSK_NO_MEMORY; } /* init sortlink for each core */ for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { ret = OsSortLinkInit(&g_percpu[index].taskSortLink);//每個(gè)CPU內(nèi)核都有一個(gè)執(zhí)行任務(wù)鏈表 if (ret != LOS_OK) { return LOS_ERRNO_TSK_NO_MEMORY; } } return LOS_OK; }
g_taskCBArray 就是個(gè)任務(wù)池,默認(rèn)創(chuàng)建128個(gè)任務(wù),常駐內(nèi)存,不被釋放。
g_losFreeTask是空閑任務(wù)鏈表,想創(chuàng)建任務(wù)時(shí)來這里申請(qǐng)一個(gè)空閑任務(wù),用完了就回收掉,繼續(xù)給后面的申請(qǐng)使用。
g_taskRecyleList是回收任務(wù)鏈表,專用來回收exit 任務(wù),任務(wù)所占資源被確認(rèn)歸還后被徹底刪除,就像員工離職一樣,得有個(gè)離職隊(duì)列和流程,要?dú)w還電腦,郵箱,有沒有借錢要還的 等操作。
對(duì)應(yīng)張大爺?shù)墓适拢河脩粢獊韴?chǎng)館領(lǐng)取表格填節(jié)目單,場(chǎng)館只準(zhǔn)備了128張表格,領(lǐng)完就沒有了,但是節(jié)目表演完了會(huì)回收表格,這樣多了一張表格就可以給其他人領(lǐng)取了,這128張表格對(duì)應(yīng)鴻蒙內(nèi)核這就是任務(wù)池,簡(jiǎn)單吧。
就緒隊(duì)列是怎么回事
CPU執(zhí)行速度是很快的,鴻蒙內(nèi)核默認(rèn)一個(gè)時(shí)間片是 10ms, 資源有限,需要在眾多任務(wù)中來回的切換,所以絕不能讓CPU等待任務(wù),CPU就像公司最大的領(lǐng)導(dǎo),下面很多的部門等領(lǐng)導(dǎo)來審批,吃飯。只有大家等領(lǐng)導(dǎo),哪有領(lǐng)導(dǎo)等你們的道理,所以工作要提前準(zhǔn)備好,每個(gè)部門的優(yōu)先級(jí)又不一樣,所以每個(gè)部門都要有個(gè)任務(wù)隊(duì)列,里面放的是領(lǐng)導(dǎo)能直接處理的任務(wù),沒準(zhǔn)備好的不要放進(jìn)來,因?yàn)檫@是給CPU提前準(zhǔn)備好的糧食!
這就是就緒隊(duì)列的原理,一共有32個(gè)就緒隊(duì)列,進(jìn)程和線程都有,因?yàn)榫€程的優(yōu)先級(jí)是默認(rèn)32個(gè), 每個(gè)隊(duì)列中放同等優(yōu)先級(jí)的task.
還是看源碼吧
#define OS_PRIORITY_QUEUE_NUM 32 LITE_OS_SEC_BSS LOS_DL_LIST *g_priQueueList = NULL;//隊(duì)列鏈表 LITE_OS_SEC_BSS UINT32 g_priQueueBitmap;//隊(duì)列位圖 UINT32每位代表一個(gè)優(yōu)先級(jí),共32個(gè)優(yōu)先級(jí) //內(nèi)部隊(duì)列初始化 UINT32 OsPriQueueInit(VOID) { UINT32 priority; /* system resident resource *///常駐內(nèi)存 g_priQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, (OS_PRIORITY_QUEUE_NUM * sizeof(LOS_DL_LIST)));//分配32個(gè)隊(duì)列頭節(jié)點(diǎn) if (g_priQueueList == NULL) { return LOS_NOK; } for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) { ? ? ? ? LOS_ListInit(&g_priQueueList[priority]);//隊(duì)列初始化,前后指針指向自己 ? ? } ? ? return LOS_OK; }
注意看g_priQueueList 的內(nèi)存分配,就是32個(gè)LOS_DL_LIST,還記得LOS_DL_LIST的妙用嗎,不清楚的去鴻蒙系統(tǒng)源碼分析(總目錄)里面翻。
對(duì)應(yīng)張大爺?shù)墓适拢壕褪情T口那些排隊(duì)的都是至少有一個(gè)節(jié)目單是符合表演標(biāo)準(zhǔn)的,資源都到位了,沒有的連排隊(duì)的資格都木有,就慢慢等吧。
任務(wù)棧是怎么回事
每個(gè)任務(wù)都是獨(dú)立開的,任務(wù)之間也相互獨(dú)立,之間通訊通過IPC,這里的“獨(dú)立”指的是每個(gè)任務(wù)都有自己的運(yùn)行環(huán)境 —— ??臻g,稱為任務(wù)棧,棧空間里保存的信息包含局部變量、寄存器、函數(shù)參數(shù)、函數(shù)返回地址等等
但系統(tǒng)中只有一個(gè)CPU,任務(wù)又是獨(dú)立的,調(diào)度的本質(zhì)就是CPU執(zhí)行一個(gè)新task,老task在什么地方被中斷誰(shuí)也不清楚,是隨機(jī)的。那如何保證老任務(wù)被再次調(diào)度選中時(shí)還能從上次被中斷的地方繼續(xù)玩下去呢?
答案是:任務(wù)上下文,CPU內(nèi)有一堆的寄存器,CPU運(yùn)行本質(zhì)的就是這些寄存器的值不斷的變化,只要切換時(shí)把這些值保存起來,再還原回去就能保證task的連續(xù)執(zhí)行,讓用戶毫無感知。鴻蒙內(nèi)核給一個(gè)任務(wù)執(zhí)行的時(shí)間是 20ms ,也就是說有多任務(wù)競(jìng)爭(zhēng)的情況下,一秒鐘內(nèi)最多要來回切換50次。
對(duì)應(yīng)張大爺?shù)墓适拢壕褪桥龅焦?jié)目沒有表演完就必須打斷的情況下,需要把當(dāng)時(shí)的情況記錄下來,比如小朋友在演躲貓貓的游戲,一半不演了,張三正在樹上,李四正在廁所躲,都記錄下來,下次再回來你們上次在哪就會(huì)哪呆著去,就位了繼續(xù)表演。這樣就接上了,觀眾就木有感覺了。
任務(wù)上下文(TaskContext)是怎樣的呢?還是直接看源碼
/* The size of this structure must be smaller than or equal to the size specified by OS_TSK_STACK_ALIGN (16 bytes). */ typedef struct { #if !defined(LOSCFG_ARCH_FPU_DISABLE) UINT64 D[FP_REGS_NUM]; /* D0-D31 */ UINT32 regFPSCR; /* FPSCR */ UINT32 regFPEXC; /* FPEXC */ #endif UINT32 resved; /* It's stack 8 aligned */ UINT32 regPSR; UINT32 R[GEN_REGS_NUM]; /* R0-R12 */ UINT32 SP; /* R13 */ UINT32 LR; /* R14 */ UINT32 PC; /* R15 */ } TaskContext;
發(fā)現(xiàn)基本都是CPU寄存器的恢復(fù)現(xiàn)場(chǎng)值, 具體各寄存器有什么作用大家可以去網(wǎng)上詳查,后續(xù)也有專門的文章來介紹。這里說其中的三個(gè)寄存器 SP, LR, PC
LR
用途有二,一是保存子程序返回地址,當(dāng)調(diào)用BL、BX、BLX等跳轉(zhuǎn)指令時(shí)會(huì)自動(dòng)保存返回地址到LR;二是保存異常發(fā)生的異常返回地址。
PC(Program Counter)
為程序計(jì)數(shù)器,用于保存程序的執(zhí)行地址,在ARM的三級(jí)流水線架構(gòu)中,程序流水線包括取址、譯碼和執(zhí)行三個(gè)階段,PC指向的是當(dāng)前取址的程序地址,所以32位ARM中,譯碼地址(正在解析還未執(zhí)行的程序)為PC-4,執(zhí)行地址(當(dāng)前正在執(zhí)行的程序地址)為PC-8, 當(dāng)突然發(fā)生中斷的時(shí)候,保存的是PC的地址。
SP
每一種異常模式都有其自己獨(dú)立的r13,它通常指向異常模式所專用的堆棧,當(dāng)ARM進(jìn)入異常模式的時(shí)候,程序就可以把一般通用寄存器壓入堆棧,返回時(shí)再出棧,保證了各種模式下程序的狀態(tài)的完整性。
任務(wù)棧初始化
任務(wù)棧的初始化就是任務(wù)上下文的初始化,因?yàn)槿蝿?wù)沒開始執(zhí)行,里面除了上下文不會(huì)有其他內(nèi)容,注意上下文存放的位置在棧的底部。初始狀態(tài)下 sp就是指向的棧底,棧頂內(nèi)容永遠(yuǎn)是 0xCCCCCCCC "燙燙燙燙",這幾個(gè)字應(yīng)該很熟悉嗎?如果不是那幾個(gè)字了,那說明棧溢出了, 后續(xù)篇會(huì)詳細(xì)說明這塊,大家也可以自行去看代碼,很有意思.
Task函數(shù)集
LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack, BOOL initFlag) { UINT32 index = 1; TaskContext *taskContext = NULL; if (initFlag == TRUE) { OsStackInit(topStack, stackSize); } taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext));//注意看上下文將存放在棧的底部 /* initialize the task context */ #ifdef LOSCFG_GDB taskContext->PC = (UINTPTR)OsTaskEntrySetupLoopFrame; #else taskContext->PC = (UINTPTR)OsTaskEntry;//程序計(jì)數(shù)器,CPU首次執(zhí)行task時(shí)跑的第一條指令位置 #endif taskContext->LR = (UINTPTR)OsTaskExit; /* LR should be kept, to distinguish it's THUMB or ARM instruction */ taskContext->resved = 0x0; taskContext->R[0] = taskID; /* R0 */ taskContext->R[index++] = 0x01010101; /* R1, 0x01010101 : reg initialed magic word */ for (; index < GEN_REGS_NUM; index++) { //R2 - R12的初始化很有意思,為什么要這么做? taskContext->R[index] = taskContext->R[index - 1] + taskContext->R[1]; /* R2 - R12 */ } #ifdef LOSCFG_INTERWORK_THUMB // 16位模式 taskContext->regPSR = PSR_MODE_SVC_THUMB; /* CPSR (Enable IRQ and FIQ interrupts, THUMNB-mode) */ #else taskContext->regPSR = PSR_MODE_SVC_ARM; /* CPSR (Enable IRQ and FIQ interrupts, ARM-mode) */ #endif #if !defined(LOSCFG_ARCH_FPU_DISABLE) /* 0xAAA0000000000000LL : float reg initialed magic word */ for (index = 0; index < FP_REGS_NUM; index++) { taskContext->D[index] = 0xAAA0000000000000LL + index; /* D0 - D31 */ } taskContext->regFPSCR = 0; taskContext->regFPEXC = FP_EN; #endif return (VOID *)taskContext; }
使用場(chǎng)景和功能
任務(wù)創(chuàng)建后,內(nèi)核可以執(zhí)行鎖任務(wù)調(diào)度,解鎖任務(wù)調(diào)度,掛起,恢復(fù),延時(shí)等操作,同時(shí)也可以設(shè)置任務(wù)優(yōu)先級(jí),獲取任務(wù)優(yōu)先級(jí)。任務(wù)結(jié)束的時(shí)候,則進(jìn)行當(dāng)前任務(wù)自刪除操作。
Huawei LiteOS 系統(tǒng)中的任務(wù)管理模塊為用戶提供下面幾種功能。
功能分類 | 接口名 | 描述 |
---|---|---|
任務(wù)的創(chuàng)建和刪除 | LOS_TaskCreateOnly | 創(chuàng)建任務(wù),并使該任務(wù)進(jìn)入suspend狀態(tài),并不調(diào)度。 |
LOS_TaskCreate | 創(chuàng)建任務(wù),并使該任務(wù)進(jìn)入ready狀態(tài),并調(diào)度。 | |
LOS_TaskDelete | 刪除指定的任務(wù)。 | |
任務(wù)狀態(tài)控制 | LOS_TaskResume | 恢復(fù)掛起的任務(wù)。 |
LOS_TaskSuspend | 掛起指定的任務(wù)。 | |
LOS_TaskDelay | 任務(wù)延時(shí)等待。 | |
LOS_TaskYield | 顯式放權(quán),調(diào)整指定優(yōu)先級(jí)的任務(wù)調(diào)度順序。 | |
任務(wù)調(diào)度的控制 | LOS_TaskLock | 鎖任務(wù)調(diào)度。 |
LOS_TaskUnlock | 解鎖任務(wù)調(diào)度。 | |
任務(wù)優(yōu)先級(jí)的控制 | LOS_CurTaskPriSet | 設(shè)置當(dāng)前任務(wù)的優(yōu)先級(jí)。 |
LOS_TaskPriSet | 設(shè)置指定任務(wù)的優(yōu)先級(jí)。 | |
LOS_TaskPriGet | 獲取指定任務(wù)的優(yōu)先級(jí)。 | |
任務(wù)信息獲取 | LOS_CurTaskIDGet | 獲取當(dāng)前任務(wù)的ID。 |
LOS_TaskInfoGet | 設(shè)置指定任務(wù)的優(yōu)先級(jí)。 | |
LOS_TaskPriGet | 獲取指定任務(wù)的信息。 | |
LOS_TaskStatusGet | 獲取指定任務(wù)的狀態(tài)。 | |
LOS_TaskNameGet | 獲取指定任務(wù)的名稱。 | |
LOS_TaskInfoMonitor | 監(jiān)控所有任務(wù),獲取所有任務(wù)的信息。 | |
LOS_NextTaskIDGet | 獲取即將被調(diào)度的任務(wù)的ID。 |
創(chuàng)建任務(wù)的過程
創(chuàng)建任務(wù)之前先了解另一個(gè)結(jié)構(gòu)體 tagTskInitParam
typedef struct tagTskInitParam {//Task的初始化參數(shù) TSK_ENTRY_FUNC pfnTaskEntry; /**< Task entrance function */ //任務(wù)的入口函數(shù) UINT16 usTaskPrio; /**< Task priority */ //任務(wù)優(yōu)先級(jí) UINT16 policy; /**< Task policy */ //任務(wù)調(diào)度方式 UINTPTR auwArgs[4]; /**< Task parameters, of which the maximum number is four */ //入口函數(shù)的參數(shù),最多四個(gè) UINT32 uwStackSize; /**< Task stack size */ //任務(wù)棧大小 CHAR *pcName; /**< Task name */ //任務(wù)名稱 #if (LOSCFG_KERNEL_SMP == YES) UINT16 usCpuAffiMask; /**< Task cpu affinity mask */ //任務(wù)cpu親和力掩碼 #endif UINT32 uwResved; /**< It is automatically deleted if set to LOS_TASK_STATUS_DETACHED. It is unable to be deleted if set to 0. */ //如果設(shè)置為L(zhǎng)OS_TASK_STATUS_DETACHED,則自動(dòng)刪除。如果設(shè)置為0,則無法刪除 UINT16 consoleID; /**< The console id of task belongs */ //任務(wù)的控制臺(tái)id所屬 UINT32 processID; //進(jìn)程ID UserTaskParam userParam; //在用戶態(tài)運(yùn)行時(shí)棧參數(shù) } TSK_INIT_PARAM_S;
這些初始化參數(shù)是外露的任務(wù)初始參數(shù),pfnTaskEntry對(duì)java來說就是你new進(jìn)程的run(),需要上層使用者提供. 看個(gè)例子吧:shell中敲 ping 命令看下它創(chuàng)建的過程
u32_t osShellPing(int argc, const char **argv) { int ret; u32_t i = 0; u32_t count = 0; int count_set = 0; u32_t interval = 1000; /* default ping interval */ u32_t data_len = 48; /* default data length */ ip4_addr_t dst_ipaddr; TSK_INIT_PARAM_S stPingTask; // ...省去一些中間代碼 /* start one task if ping forever or ping count greater than 60 */ if (count == 0 || count > LWIP_SHELL_CMD_PING_RETRY_TIMES) { if (ping_taskid > 0) { PRINTK("Ping task already running and only support one now\n"); return LOS_NOK; } stPingTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ping_cmd;//線程的執(zhí)行函數(shù) stPingTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//0x4000 = 16K stPingTask.pcName = "ping_task"; stPingTask.usTaskPrio = 8; /* higher than shell 優(yōu)先級(jí)高于10,屬于內(nèi)核態(tài)線程*/ stPingTask.uwResved = LOS_TASK_STATUS_DETACHED; stPingTask.auwArgs[0] = dst_ipaddr.addr; /* network order */ stPingTask.auwArgs[1] = count; stPingTask.auwArgs[2] = interval; stPingTask.auwArgs[3] = data_len; ret = LOS_TaskCreate((UINT32 *)(&ping_taskid), &stPingTask); } // ... return LOS_OK; ping_error: lwip_ping_usage(); return LOS_NOK; }
發(fā)現(xiàn)ping的調(diào)度優(yōu)先級(jí)是8,比shell 還高,那shell的是多少?答案是:看源碼是 9
LITE_OS_SEC_TEXT_MINOR UINT32 ShellTaskInit(ShellCB *shellCB) { CHAR *name = NULL; TSK_INIT_PARAM_S initParam = { 0}; if (shellCB->consoleID == CONSOLE_SERIAL) { name = SERIAL_SHELL_TASK_NAME; } else if (shellCB->consoleID == CONSOLE_TELNET) { name = TELNET_SHELL_TASK_NAME; } else { return LOS_NOK; } initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellTask; initParam.usTaskPrio = 9; /* 9:shell task priority */ initParam.auwArgs[0] = (UINTPTR)shellCB; initParam.uwStackSize = 0x3000; initParam.pcName = name; initParam.uwResved = LOS_TASK_STATUS_DETACHED; (VOID)LOS_EventInit(&shellCB->shellEvent); return LOS_TaskCreate(&shellCB->shellTaskHandle, &initParam); }
關(guān)于shell后續(xù)會(huì)詳細(xì)介紹,請(qǐng)持續(xù)關(guān)注。
前置條件了解清楚后,具體看任務(wù)是如何一步步創(chuàng)建的,如何和進(jìn)程綁定,加入調(diào)度就緒隊(duì)列,還是繼續(xù)看源碼
//創(chuàng)建Task LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskID, TSK_INIT_PARAM_S *initParam) { UINT32 ret; UINT32 intSave; LosTaskCB *taskCB = NULL; if (initParam == NULL) { return LOS_ERRNO_TSK_PTR_NULL; } if (OS_INT_ACTIVE) { return LOS_ERRNO_TSK_YIELD_IN_INT; } if (initParam->uwResved & OS_TASK_FLAG_IDLEFLAG) {//OS_TASK_FLAG_IDLEFLAG 是屬于內(nèi)核 idle進(jìn)程專用的 initParam->processID = OsGetIdleProcessID();//獲取空閑進(jìn)程 } else if (OsProcessIsUserMode(OsCurrProcessGet())) {//當(dāng)前進(jìn)程是否為用戶模式 initParam->processID = OsGetKernelInitProcessID();//不是就取"Kernel"進(jìn)程 } else { initParam->processID = OsCurrProcessGet()->processID;//獲取當(dāng)前進(jìn)程 ID賦值 } initParam->uwResved &= ~OS_TASK_FLAG_IDLEFLAG;//不能是 OS_TASK_FLAG_IDLEFLAG initParam->uwResved &= ~OS_TASK_FLAG_PTHREAD_JOIN;//不能是 OS_TASK_FLAG_PTHREAD_JOIN if (initParam->uwResved & LOS_TASK_STATUS_DETACHED) {//是否設(shè)置了自動(dòng)刪除 initParam->uwResved = OS_TASK_FLAG_DETACHED;//自動(dòng)刪除,注意這里是 = ,也就是說只有 OS_TASK_FLAG_DETACHED 一個(gè)標(biāo)簽了 } ret = LOS_TaskCreateOnly(taskID, initParam);//創(chuàng)建一個(gè)任務(wù),這是任務(wù)創(chuàng)建的實(shí)體,前面都只是前期準(zhǔn)備工作 if (ret != LOS_OK) { return ret; } taskCB = OS_TCB_FROM_TID(*taskID);//通過ID拿到task實(shí)體 SCHEDULER_LOCK(intSave); taskCB->taskStatus &= ~OS_TASK_STATUS_INIT;//任務(wù)不再是初始化 OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB, 0);//進(jìn)入調(diào)度就緒隊(duì)列,新任務(wù)是直接進(jìn)入就緒隊(duì)列的 SCHEDULER_UNLOCK(intSave); /* in case created task not running on this core, schedule or not depends on other schedulers status. */ LOS_MpSchedule(OS_MP_CPU_ALL);//如果創(chuàng)建的任務(wù)沒有在這個(gè)核心上運(yùn)行,是否調(diào)度取決于其他調(diào)度程序的狀態(tài)。 if (OS_SCHEDULER_ACTIVE) {//當(dāng)前CPU核處于可調(diào)度狀態(tài) LOS_Schedule();//發(fā)起調(diào)度 } return LOS_OK; }
對(duì)應(yīng)張大爺?shù)墓适拢壕褪枪?jié)目單要怎么填,按格式來,從哪里開始演,要多大的空間,王場(chǎng)館好協(xié)調(diào)好現(xiàn)場(chǎng)的環(huán)境。這里注意在同一個(gè)節(jié)目單只要節(jié)目沒演完,王場(chǎng)館申請(qǐng)場(chǎng)地的空間就不能給別人用,這個(gè)場(chǎng)地空間對(duì)應(yīng)的就是鴻蒙任務(wù)的??臻g,除非整個(gè)節(jié)目單都完了,就回收了。把整個(gè)場(chǎng)地干干凈凈的留給下一個(gè)人的節(jié)目單來表演。
至此的創(chuàng)建已經(jīng)完成,已各就各位,源碼最后還申請(qǐng)了一次LOS_Schedule();因?yàn)轼櫭傻恼{(diào)度方式是搶占式的,如何本次task的任務(wù)優(yōu)先級(jí)高于其他就緒隊(duì)列,那么接下來要執(zhí)行的任務(wù)就是它了!
編輯:hfy
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2638瀏覽量
67563
發(fā)布評(píng)論請(qǐng)先 登錄
【HarmonyOS】鴻蒙內(nèi)核源碼分析(內(nèi)存管理篇)
鴻蒙內(nèi)核源碼分析:用通俗易懂的語(yǔ)言告訴你鴻蒙內(nèi)核發(fā)生了什么?
鴻蒙內(nèi)核源碼分析(源碼注釋篇):給HarmonyOS源碼逐行加上中文注釋
鴻蒙源碼分析系列(總目錄) | 給HarmonyOS源碼逐行加上中文注釋
鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇) :手眼通天的虛擬內(nèi)存
鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇) :手眼通天的虛擬內(nèi)存
鴻蒙內(nèi)核源碼分析(必讀篇):用故事說內(nèi)核
鴻蒙內(nèi)核源碼分析(調(diào)度機(jī)制篇):Task是如何被調(diào)度執(zhí)行的
鴻蒙內(nèi)核源碼分析(Task管理篇):task是內(nèi)核調(diào)度的單元
鴻蒙內(nèi)核源碼分析(Task管理篇):task是內(nèi)核調(diào)度的單元
鴻蒙內(nèi)核源碼分析(必讀篇)
鴻蒙內(nèi)核源碼分析:鴻蒙內(nèi)核的每段匯編代碼解析

鴻蒙內(nèi)核源碼分析: 虛擬內(nèi)存和物理內(nèi)存是怎么管理的

鴻蒙內(nèi)核源碼分析 :內(nèi)核最重要結(jié)構(gòu)體

評(píng)論