寝不屍,居不容。見齊衰者,雖狎,必變。見冕者與瞽者,雖亵,必以貌。兇服者式之。式負版者。有盛馔,必變色而作。迅雷風烈,必變 《論語》:鄉黨篇
百篇部落格系列篇.本篇為:
v46.xx 鴻蒙核心源碼分析(特殊程序篇) | 老鼠生兒會打洞
程序管理相關篇為:
- v02.06 鴻蒙核心源碼分析(程序管理) | 誰在管理核心資源
- v24.03 鴻蒙核心源碼分析(程序概念) | 程序在管理哪些資源
- v45.05 鴻蒙核心源碼分析(Fork) | 一次調用,兩次傳回
- v46.05 鴻蒙核心源碼分析(特殊程序) | 老鼠生兒會打洞
- v47.02 鴻蒙核心源碼分析(程序回收) | 臨終前如何向老祖宗托孤
- v48.05 鴻蒙核心源碼分析(信号生産) | 年過半百,依然活力十足
- v49.03 鴻蒙核心源碼分析(信号消費) | 誰讓CPU連續四次換棧運作
- v71.03 鴻蒙核心源碼分析(Shell編輯) | 兩個任務,三個階段
- v72.01 鴻蒙核心源碼分析(Shell解析) | 應用窺伺核心的視窗
三個程序
鴻蒙有三個特殊的程序,建立順序如下:
- 2号程序,
,為核心态根程序.啟動過程中建立.KProcess
- 0号程序,
為核心态第二個程序,它是通過KIdle
fork 而來的.這有點難了解.KProcess
- 1号程序,
,為使用者态根程序.由任務init
建立.SystemInit
- 發現沒有在圖中看不到0号程序,在看完本篇之後請想想為什麼?
家族式管理
- 程序(process)是家族式管理,總體分為兩大家族,使用者态家族和核心态家族.
- 使用者态的程序是平民階層,屌絲矮矬窮,幹着各行各業的活,權利有限,人數衆多,活動範圍有限(使用者空間).有關機關肯定不能随便進出.這個階層有個共同的老祖宗g_userInitProcess (1号程序).
g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 *///使用者态的根程序 //擷取使用者态程序的根程序,所有使用者程序都是g_processCBArray[g_userInitProcess] fork來的 LITE_OS_SEC_TEXT UINT32 OsGetUserInitProcessID(VOID) { return g_userInitProcess; }
- 核心态的程序是貴族階層,管理平民階層的,維持平民生活秩序的,擁有超級權限,能通路整個空間和所有資源,人數不多.這個階層老祖宗是 g_kernelInitProcess(2号程序).
g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 *///核心态的根程序 //擷取核心态程序的根程序,所有核心程序都是g_processCBArray[g_kernelInitProcess] fork來的,包括g_processCBArray[g_kernelIdleProcess]程序 LITE_OS_SEC_TEXT UINT32 OsGetKernelInitProcessID(VOID) { return g_kernelInitProcess; }
- 兩位老祖宗都不是通過fork來的,而是核心強制規定程序ID号,強制寫死基因建立的.
- 這兩個階層可以互相流動嗎,有沒有可能通過聯考改變命運? 答案是: 絕對冇可能!!! 龍生龍,鳳生鳳,老鼠生兒會打洞.從老祖宗建立的那一刻起就被刻在基因裡了,抹不掉了. 因為後續所有的程序都是由這兩位老同志克隆(clone)來的,沒得商量的繼承這份基因.
有專門的标簽來LosProcessCB
區分這兩個階層.整個鴻蒙核心源碼并沒有提供改變命運機會的processMode
函數.set
#define OS_KERNEL_MODE 0x0U //核心态 #define OS_USER_MODE 0x1U //使用者态 STATIC INLINE BOOL OsProcessIsUserMode(const LosProcessCB *processCB)//使用者模式程序 { return (processCB->processMode == OS_USER_MODE); } typedef struct ProcessCB { // ... UINT16 processMode; /**< Kernel Mode:0; User Mode:1; */ //0位核心态,1為使用者态程序 } LosProcessCB;
2号程序 KProcess
2号程序為核心态的老祖宗,是核心建立的首個程序,源碼過程如下,省略了不相幹的代碼.
bl main @帶LR的子程式跳轉, LR = pc - 4 ,執行C層main函數
/******************************************************************************
核心入口函數,由彙編調用,見于reset_vector_up.S 和 reset_vector_mp.S
up指單核CPU, mp指多核CPU bl main
******************************************************************************/
LITE_OS_SEC_TEXT_INIT INT32 main(VOID)//由主CPU執行,預設0号CPU 為主CPU
{
// ... 省略
uwRet = OsMain();// 核心各子產品初始化
}
LITE_OS_SEC_TEXT_INIT INT32 OsMain(VOID)
{
// ...
ret = OsKernelInitProcess();// 建立核心态根程序
// ...
ret = OsSystemInit(); //中間建立了使用者态根程序
}
//初始化 2号程序,即核心态程序的老祖宗
LITE_OS_SEC_TEXT_INIT UINT32 OsKernelInitProcess(VOID)
{
LosProcessCB *processCB = NULL;
UINT32 ret;
ret = OsProcessInit();// 初始化程序子產品全部變量,建立各循環雙向連結清單
if (ret != LOS_OK) {
return ret;
}
processCB = OS_PCB_FROM_PID(g_kernelInitProcess);// 以PID方式得到一個程序
ret = OsProcessCreateInit(processCB, OS_KERNEL_MODE, "KProcess", 0);// 初始化程序,最高優先級0,鴻蒙程序一共有32個優先級(0-31) 其中0-9級為核心程序,使用者程序可配置的優先級有22個(10-31)
if (ret != LOS_OK) {
return ret;
}
processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;// 程序初始化位 置1
g_processGroup = processCB->group;//全局程序組指向了KProcess所在的程序組
LOS_ListInit(&g_processGroup->groupList);// 程序組連結清單初始化
OsCurrProcessSet(processCB);// 設定為目前程序
return OsCreateIdleProcess();// 建立一個空閑狀态的程序
}
解讀
- main函數在系列篇中會單獨講,請留意自行翻看,它是在開機之初在SVC模式下建立的.
- 核心态老祖宗的名字叫
,優先級為最高 0 級,KProcess
程序是長期活躍的,很多重要的任務都會跑在其之下.例如:KProcess
-
Swt_Task
-
oom_task
-
system_wq
-
tcpip_thread
-
SendToSer
-
SendToTelnet
-
eth_irq_task
-
TouchEventHandler
-
此處不細講這些任務,在其他篇幅有介紹,但光看名字也能猜個八九,請自行翻看.USB_GIANT_Task
-
- 緊接着
以KProcess
的方式 fork了一個 名為CLONE_FILES
的子程序(0号程序).KIdle
- 核心态的所有程序都來自2号程序這位老同志,子子孫孫,代代相傳,形成一顆家族樹,和人類的傳承所不同的是,它們往往是白發人送黑發人,子孫程序往往都是短命鬼,老祖宗最能活,子孫都死絕了它還在,有些收屍的工作要交給它幹.
0 号程序 KIdle
0号程序是核心建立的第二個程序,在
OsKernelInitProcess
的末尾将
KProcess
設為目前程序後,緊接着就
fork
了0号程序.為什麼一定要先設定目前程序,因為fork需要一個父程序,而此時系統處于啟動階段,并沒有目前程序. 是的,您沒有看錯.程序是作業系統為友善管理資源而衍生出來的概念,系統并不是非要程序,任務才能運作的. 開機階段就是啥都沒有,預設跑在svc模式下,預設起始位址
reset_vector
都是由硬體上電後規定的. 程序,線程都是跑起來後慢慢賦予的意義.
OsCurrProcessSet
是從軟體層面賦予了此為目前程序的這個概念.
KProcess
是核心設定的第一個目前程序.有了它,就可以fork, fork, fork !
//建立一個名叫"KIdle"的0号程序,給CPU空閑的時候使用
STATIC UINT32 OsCreateIdleProcess(VOID)
{
UINT32 ret;
CHAR *idleName = "Idle";
LosProcessCB *idleProcess = NULL;
Percpu *perCpu = OsPercpuGet();
UINT32 *idleTaskID = &perCpu->idleTaskID;//得到CPU的idle task
ret = OsCreateResourceFreeTask();// 建立一個資源回收任務,優先級為5 用于回收程序退出時的各種資源
if (ret != LOS_OK) {
return ret;
}
//建立一個名叫"KIdle"的程序,并建立一個idle task,CPU空閑的時候就待在 idle task中等待被喚醒
ret = LOS_Fork(CLONE_FILES, "KIdle", (TSK_ENTRY_FUNC)OsIdleTask, LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE);
if (ret < 0) {//核心程序的fork并不會一次調用,傳回兩次,此子程序執行的開始位置是參數OsIdleTask
return LOS_NOK;
}
g_kernelIdleProcess = (UINT32)ret;//傳回 0号程序
idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);//通過ID拿到程序實體
*idleTaskID = idleProcess->threadGroupID;//綁定CPU的IdleTask,或者說改變CPU現有的idle任務
OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;//設定Idle task 為一個系統任務
#if (LOSCFG_KERNEL_SMP == YES)
OS_TCB_FROM_TID(*idleTaskID)->cpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());//多核CPU的任務指定,防止亂串了,注意多核才會有并行處理
#endif
(VOID)memset_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, 0, OS_TCB_NAME_LEN);//task 名字先清0
(VOID)memcpy_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, idleName, strlen(idleName));//task 名字叫 idle
return LOS_OK;
}
- 看過fork篇的可能發現了一個參數,
被建立的方式和通過系統調用建立的方式不一樣,一個用的是KIdle
,一個是CLONE_FILES
具體的建立方式如下:CLONE_SIGHAND
#define CLONE_VM 0x00000100 //子程序與父程序運作于相同的記憶體空間 #define CLONE_FS 0x00000200 //子程序與父程序共享相同的檔案系統,包括root、目前目錄、umask #define CLONE_FILES 0x00000400 //子程序與父程序共享相同的檔案描述符(file descriptor)表 #define CLONE_SIGHAND 0x00000800 //子程序與父程序共享相同的信号處理(signal handler)表 #define CLONE_PTRACE 0x00002000 //若父程序被trace,子程序也被trace #define CLONE_VFORK 0x00004000 //父程序被挂起,直至子程序釋放虛拟記憶體資源 #define CLONE_PARENT 0x00008000 //建立的子程序的父程序是調用者的父程序,新程序與建立它的程序成了“兄弟”而不是“父子” #define CLONE_THREAD 0x00010000 //Linux 2.4中增加以支援POSIX線程标準,子程序與父程序共享相同的線程群
-
建立了一個名為KIdle
的任務,任務的入口函數為Idle
,這是個空閑任務,啥也不幹的.專門用來給cpu休息的,cpu空閑時就待在這個任務裡等活幹.OsIdleTask
LITE_OS_SEC_TEXT WEAK VOID OsIdleTask(VOID) { while (1) {//隻有一個死循環 #ifdef LOSCFG_KERNEL_TICKLESS //低功耗模式開關, idle task 中關閉tick if (OsTickIrqFlagGet()) { OsTickIrqFlagSet(0); OsTicklessStart(); } #endif Wfi();//WFI指令:arm core 立即進入low-power standby state,進入休眠模式,等待中斷. } }
- fork 核心态程序和fork使用者态程序有個地方會不一樣,就是SP寄存器的值.fork使用者态的程序一次調用兩次傳回(父子程序各一次),傳回的位置一樣(是因為拷貝了父程序陷入核心時的上下文).是以可以通過傳回值來判斷是父還是子傳回.這個在fork篇中有詳細的描述.請自行翻看. 但fork核心态程序雖也有兩次傳回,但是傳回的位置卻不一樣,子程序的傳回位置是由核心指定的,例如:
任務的入口函數為Idle
.詳見代碼:OsIdleTask
//任務初始化時拷貝任務資訊 STATIC VOID OsInitCopyTaskParam(LosProcessCB *childProcessCB, const CHAR *name, UINTPTR entry, UINT32 size, TSK_INIT_PARAM_S *childPara) { LosTaskCB *mainThread = NULL; UINT32 intSave; SCHEDULER_LOCK(intSave); mainThread = OsCurrTaskGet();//擷取目前task,注意變量名從這裡也可以看出 thread 和 task 是一個概念,隻是核心常說task,上層應用說thread ,概念的映射. if (OsProcessIsUserMode(childProcessCB)) {//使用者态程序 childPara->pfnTaskEntry = mainThread->taskEntry;//拷貝目前任務入口位址 childPara->uwStackSize = mainThread->stackSize; //棧空間大小 childPara->userParam.userArea = mainThread->userArea; //使用者态棧區棧頂位置 childPara->userParam.userMapBase = mainThread->userMapBase; //使用者态棧底 childPara->userParam.userMapSize = mainThread->userMapSize; //使用者态棧大小 } else {//注意核心态程序建立任務的入口由外界指定,例如 OsCreateIdleProcess 指定了OsIdleTask childPara->pfnTaskEntry = (TSK_ENTRY_FUNC)entry;//參數(sp)為核心态入口位址 childPara->uwStackSize = size;//參數(size)為核心态棧大小 } childPara->pcName = (CHAR *)name; //拷貝程序名字 childPara->policy = mainThread->policy; //拷貝排程模式 childPara->usTaskPrio = mainThread->priority; //拷貝優先級 childPara->processID = childProcessCB->processID; //拷貝程序ID if (mainThread->taskStatus & OS_TASK_FLAG_PTHREAD_JOIN) { childPara->uwResved = OS_TASK_FLAG_PTHREAD_JOIN; } else if (mainThread->taskStatus & OS_TASK_FLAG_DETACHED) { childPara->uwResved = OS_TASK_FLAG_DETACHED; } SCHEDULER_UNLOCK(intSave); }
- 結論是建立0号程序中的
調用OsCreateIdleProcess
後隻會有一次傳回.而且傳回值為0,因為LOS_Fork
中0号程序還沒有被配置設定.詳見代碼,注意看最後的注釋:g_freeProcess
//程序子產品初始化,被編譯放在代碼段 .init 中 LITE_OS_SEC_TEXT_INIT UINT32 OsProcessInit(VOID) { UINT32 index; UINT32 size; g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;//預設支援64個程序 size = g_processMaxNum * sizeof(LosProcessCB);//算出總大小 g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);// 程序池,占用核心堆,記憶體池配置設定 if (g_processCBArray == NULL) { return LOS_NOK; } (VOID)memset_s(g_processCBArray, size, 0, size);//安全方式重置清0 LOS_ListInit(&g_freeProcess);//程序空閑連結清單初始化,建立一個程序時從g_freeProcess中申請一個程序描述符使用 LOS_ListInit(&g_processRecyleList);//程序回收連結清單初始化,回收完成後進入g_freeProcess等待再次被申請使用 for (index = 0; index < g_processMaxNum; index++) {//程序池循環建立 g_processCBArray[index].processID = index;//程序ID[0-g_processMaxNum-1]指派 g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;// 預設都是白紙一張,貼上未使用标簽 LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);//注意g_freeProcess挂的是pendList節點,是以使用要通過OS_PCB_FROM_PENDLIST找到程序實體. } g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 *///使用者态的根程序 LOS_ListDelete(&g_processCBArray[g_userInitProcess].pendList);// 将1号程序從空閑連結清單上摘出去 g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 *///核心态的根程序 LOS_ListDelete(&g_processCBArray[g_kernelInitProcess].pendList);// 将2号程序從空閑連結清單上摘出去 //注意:這波騷操作之後,g_freeProcess連結清單上還有,0,3,4,...g_processMaxNum-1号程序.建立程序是從g_freeProcess上申請 //即下次申請到的将是0号程序,而 OsCreateIdleProcess 将占有0号程序. return LOS_OK; }
1号程序 init
1号程序為使用者态的老祖宗.建立過程如下, 省略了不相幹的代碼.
LITE_OS_SEC_TEXT_INIT INT32 OsMain(VOID)
{
// ...
ret = OsKernelInitProcess();// 建立核心态根程序
// ...
ret = OsSystemInit(); //中間建立了使用者态根程序
}
UINT32 OsSystemInit(VOID)
{
//..
ret = OsSystemInitTaskCreate();//建立了一個系統任務,
}
STATIC UINT32 OsSystemInitTaskCreate(VOID)
{
UINT32 taskID;
TSK_INIT_PARAM_S sysTask;
(VOID)memset_s(&sysTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
sysTask.pfnTaskEntry = (TSK_ENTRY_FUNC)SystemInit;//任務的入口函數,這個函數實作由外部提供
sysTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//16K
sysTask.pcName = "SystemInit";//任務的名稱
sysTask.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;// 核心預設優先級為10
sysTask.uwResved = LOS_TASK_STATUS_DETACHED;//任務分離模式
#if (LOSCFG_KERNEL_SMP == YES)
sysTask.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());//cpu 親和性設定,記錄執行過任務的CPU,盡量確定由同一個CPU完成任務周期
#endif
return LOS_TaskCreate(&taskID, &sysTask);//建立任務并加入就緒隊列,并立即參與排程
}
//SystemInit的實作由由外部提供 比如..\vendor\hi3516dv300\module_init\src\system_init.c
void SystemInit(void)
{
// ...
if (OsUserInitProcess()) {//建立使用者态程序的老祖宗
PRINT_ERR("Create user init process faialed!\n");
return;
}
}
//使用者态根程序的建立過程
LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
INT32 ret;
UINT32 size;
TSK_INIT_PARAM_S param = { 0 };
VOID *stack = NULL;
VOID *userText = NULL;
CHAR *userInitTextStart = (CHAR *)&__user_init_entry;//代碼區開始位置 ,對應 LITE_USER_SEC_ENTRY
CHAR *userInitBssStart = (CHAR *)&__user_init_bss;// 未初始化資料區(BSS)。在運作時改變其值 對應 LITE_USER_SEC_BSS
CHAR *userInitEnd = (CHAR *)&__user_init_end;// 結束位址
UINT32 initBssSize = userInitEnd - userInitBssStart;
UINT32 initSize = userInitEnd - userInitTextStart;
LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);//"Init程序的優先級是 28"
ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY);// 初始化使用者程序,它将是所有應用程式的父程序
if (ret != LOS_OK) {
return ret;
}
userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);// 配置設定連續的實體頁
if (userText == NULL) {
ret = LOS_NOK;
goto ERROR;
}
(VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);// 安全copy 經加載器load的結果 __user_init_load_addr -> userText
ret = LOS_VaddrToPaddrMmap(processCB->vmSpace, (VADDR_T)(UINTPTR)userInitTextStart, LOS_PaddrQuery(userText),
initSize, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
VM_MAP_REGION_FLAG_PERM_EXECUTE | VM_MAP_REGION_FLAG_PERM_USER);// 虛拟位址與實體位址的映射
if (ret < 0) {
goto ERROR;
}
(VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);// 除了代碼段,其餘都清0
stack = OsUserInitStackAlloc(g_userInitProcess, &size);//配置設定任務在使用者态下的運作棧,大小為1M
if (stack == NULL) {
PRINTK("user init process malloc user stack failed!\n");
ret = LOS_NOK;
goto ERROR;
}
param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;// 從代碼區開始執行,也就是應用程式main 函數的位置
param.userParam.userSP = (UINTPTR)stack + size;// 使用者态棧底
param.userParam.userMapBase = (UINTPTR)stack;// 使用者态棧頂
param.userParam.userMapSize = size;// 使用者态棧大小
param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;// 可結合的(joinable)能夠被其他線程收回其資源和殺死
ret = OsUserInitProcessStart(g_userInitProcess, ¶m);// 建立一個任務,來運作main函數
if (ret != LOS_OK) {
(VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize);
goto ERROR;
}
return LOS_OK;
ERROR:
(VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);//釋放實體記憶體塊
OsDeInitPCB(processCB);//删除PCB塊
return ret;
}
- 從代碼中可以看出使用者态的老祖宗建立過程有點意思,首先它的源頭和核心态老祖宗一樣都在
.OsMain
- 通過建立一個分離模式,優先級為10的系統任務
,來完成.任務的入口函數SystemInit
的實作由平台內建商來指定. 本篇采用了SystemInit()
的實作.也就是說使用者态祖宗的建立是在hi3516dv300
棧中完成的.這個任務歸屬于核心程序sysTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//16K
KProcess
- 使用者态老祖宗的名字叫
,優先級為28級.Init
- 使用者态的每個程序有獨立的虛拟程序空間
,擁有獨立的記憶體映射表(L1,L2表),申請的記憶體需要重新映射,映射過程在記憶體系列篇中有詳細的說明.vmSpace
-
建立了一個任務,任務的入口位址為init
,由編譯器指定.__user_init_entry
- 使用者态程序是指應有程式運作的程序,通過動态加載ELF檔案的方式啟動.具體加載流程系列篇有講解,不細說.使用者态程序運作在使用者空間,但通過系統調用可陷入核心空間.具體看這張圖:
百篇部落格分析.深挖核心地基
- 給鴻蒙核心源碼加注釋過程中,整理出以下文章。内容立足源碼,常以生活場景打比方盡可能多的将核心知識點置入某種場景,具有畫面感,容易了解記憶。說别人能聽得懂的話很重要! 百篇部落格絕不是百度教條式的在說一堆诘屈聱牙的概念,那沒什麼意思。更希望讓核心變得栩栩如生,倍感親切.确實有難度,自不量力,但已經出發,回頭已是不可能的了。 😛
- 與代碼有bug需不斷debug一樣,文章和注解内容會存在不少錯漏之處,請多包涵,但會反複修正,持續更新,v**.xx 代表文章序号和修改的次數,精雕細琢,言簡意赅,力求打造精品内容。
按功能子產品:
基礎工具 | 加載運作 | 程序管理 | 編譯建構 |
---|---|---|---|
雙向連結清單 位圖管理 用棧方式 定時器 原子操作 時間管理 | ELF格式 ELF解析 靜态連結 重定位 程序映像 | 程序概念 Fork 特殊程序 程序回收 信号生産 信号消費 Shell編輯 Shell解析 | 編譯環境 編譯過程 環境腳本 建構工具 gn應用 忍者ninja |
程序通訊 | 記憶體管理 | 前因後果 | 任務管理 |
自旋鎖 互斥鎖 信号量 事件控制 消息隊列 | 記憶體配置設定 記憶體彙編 記憶體映射 記憶體規則 實體記憶體 | 總目錄 排程故事 記憶體主奴 源碼注釋 源碼結構 靜态站點 | 時鐘任務 任務排程 排程隊列 排程機制 線程概念 并發并行 CPU 系統調用 任務切換 |
檔案系統 | 硬體架構 | ||
檔案概念 索引節點 挂載目錄 根檔案系統 字元裝置 VFS 檔案句柄 管道檔案 | 彙編基礎 彙編傳參 工作模式 寄存器 異常接管 彙編彙總 中斷切換 中斷概念 中斷管理 |
百萬漢字注解.精讀核心源碼
四大碼倉中文注解 . 定期同步官方代碼
鴻蒙研究站( weharmonyos ) | 每天死磕一點點,原創不易,歡迎轉載,請注明出處。若能支援點贊更好,感謝每一份支援。