天天看點

v49.03 鴻蒙核心源碼分析(信号消費篇) | 誰讓CPU連續四次換棧運作 | 百篇部落格分析OpenHarmony源碼

子曰:“回也非助我者也,于吾言無所不說。” 《論語》:先進篇

v49.03 鴻蒙核心源碼分析(信号消費篇) | 誰讓CPU連續四次換棧運作 | 百篇部落格分析OpenHarmony源碼

百篇部落格系列篇.本篇為:

v49.xx 鴻蒙核心源碼分析(信号消費篇) | 誰讓CPU連續四次換棧運作

程序管理相關篇為:

  • v02.06 鴻蒙核心源碼分析(程序管理) | 誰在管理核心資源
  • v24.03 鴻蒙核心源碼分析(程序概念) | 程序在管理哪些資源
  • v45.05 鴻蒙核心源碼分析(Fork) | 一次調用,兩次傳回
  • v46.05 鴻蒙核心源碼分析(特殊程序) | 老鼠生兒會打洞
  • v47.02 鴻蒙核心源碼分析(程序回收) | 臨終前如何向老祖宗托孤
  • v48.05 鴻蒙核心源碼分析(信号生産) | 年過半百,依然活力十足
  • v49.03 鴻蒙核心源碼分析(信号消費) | 誰讓CPU連續四次換棧運作
  • v71.03 鴻蒙核心源碼分析(Shell編輯) | 兩個任務,三個階段
  • v72.01 鴻蒙核心源碼分析(Shell解析) | 應用窺伺核心的視窗

信号消費

本篇為信号消費篇,讀之前建議先閱讀信号生産篇,信号部分姊妹篇如下:

  • v48.xx (信号生産篇) | 年過半百,依然活力十足
  • v49.xx (信号消費篇) | 誰讓CPU連續四次換棧運作

本篇有相當的難度,涉及使用者棧和核心棧的兩輪切換,CPU四次換棧,寄存器改值,将圍繞下圖來說明.

v49.03 鴻蒙核心源碼分析(信号消費篇) | 誰讓CPU連續四次換棧運作 | 百篇部落格分析OpenHarmony源碼

解讀

  • 為本篇了解友善,把圖做簡化标簽說明:
    • user:使用者空間
    • kernel:核心空間
    • source(...):源函數
    • sighandle(...):信号處理函數,
    • syscall(...):系統調用,參數為系統調用号,如sigreturn,N(表任意)
    • user.source():表示在使用者空間運作的源函數
  • 系列篇已多次說過,使用者态的任務有兩個運作棧,一個是使用者棧,一個是核心棧.棧空間分别來自使用者空間和核心空間.兩種空間是有嚴格的位址劃分的,通過虛拟位址的大小就能判斷出是使用者空間還是核心空間.系統調用本質上是軟中斷,它使CPU執行指令的場地由使用者棧變成核心棧.怎麼變的并不複雜,就是改變(sp和cpsr寄存器的值).sp指向哪個棧就代表在哪個棧運作, 當cpu在使用者棧運作時是不能通路核心空間的,但核心态任務可以通路整個空間,而且核心态任務沒有使用者棧.
  • 了解了上面的說明,再來說下正常系統調用流程是這樣的: user.source() -> kernel.syscall(N) - > user.source() ,想要回到user.source()繼續運作,就必須儲存使用者棧現場各寄存器的值.這些值儲存在核心棧中,恢複也是從核心棧恢複.
  • 信号消費的過程的上圖可簡化表示為: user.source() -> kernel.syscall(N) ->user.sighandle() ->kernel.syscall(sigreturn) -> user.source() 在原本要回到user.source()的中間插入了信号處理函數的調用. 這正是本篇要通過代碼來說清楚的核心問題.
  • 順着這個思路可以推到以下幾點,實際也是這麼做的:
    • kernel.syscall(N) 中必須要再次儲存user.source()的上下文

      sig_switch_context

      ,為何已經儲存了一次還要再儲存一次?
    • 因為第一次是儲存在核心棧中,而核心棧這部分資料會因回到使用者态user.sighandle()運作而被恢複現場出棧了.儲存現場/恢複現場是成雙出隊的好基友,注意有些文章說會把整個核心棧清空,這是不對的.
    • 第二次儲存在任務結構體中,任務來源于任務池,是核心全局變量,常駐記憶體的.兩次儲存的都是user.source()運作時現場資訊,再回顧下相關的結構體.關鍵是

      sig_switch_context

typedef struct {
    // ...
    sig_cb  sig;//信号控制塊,用于異步通信
} LosTaskCB;
typedef struct {//信号控制塊(描述符)
    sigset_t sigFlag;		//不屏蔽的信号集
    sigset_t sigPendFlag;	//信号阻塞标簽集,記錄那些信号來過,任務依然阻塞的集合.即:這些信号不能喚醒任務
    sigset_t sigprocmask; /* Signals that are blocked            */	//任務屏蔽了哪些信号
    sq_queue_t sigactionq;	//信号捕捉隊列					
    LOS_DL_LIST waitList;	//等待連結清單,上面挂的是等待信号到來的任務, 請查找 OsTaskWait(&sigcb->waitList, timeout, TRUE)	了解						
    sigset_t sigwaitmask; /* Waiting for pending signals         */	//任務在等待哪些信号的到來
    siginfo_t sigunbinfo; /* Signal info when task unblocked     */	//任務解鎖時的信号資訊
    sig_switch_context context;	//信号切換上下文, 用于儲存切換現場, 比如發生系統調用時的傳回,涉及同一個任務的兩個棧進行切換			
} sig_cb;
           
  • 還必須要改變原有PC/R0/R1寄存器的值.想要執行user.sighandle(),PC寄存器就必須指向它,而R0,R1就是它的參數.
  • 信号處理完成後須回到核心态,怎麼再次陷入核心态? 答案是:

    __NR_sigreturn

    ,這也是個系統調用.回來後還原

    sig_switch_context

    ,即還原user.source()被打斷時SP/PC等寄存器的值,使其跳回到使用者棧從user.source()的被打斷處繼續執行.
  • 有了這三個推論,再了解下面的代碼就是吹灰之力了,涉及三個關鍵函數

    OsArmA32SyscallHandle

    OsSaveSignalContext

    OsRestorSignalContext

    本篇一一解讀,徹底挖透.先看信号上下文結構體

    sig_switch_context

    .

sig_switch_context

//任務中斷上下文
#define TASK_IRQ_CONTEXT \
        unsigned int R0;     \
        unsigned int R1;     \
        unsigned int R2;     \
        unsigned int R3;     \
        unsigned int R12;    \
        unsigned int USP;    \
        unsigned int ULR;    \
        unsigned int CPSR;   \
        unsigned int PC;

typedef struct {//信号切換上下文
    TASK_IRQ_CONTEXT
    unsigned int R7;	//存放系統調用的ID
    unsigned int count;	//記錄是否儲存了信号上下文
} sig_switch_context;
           
  • 儲存user.source()現場的結構體,

    USP

    ULR

    代表使用者棧指針和傳回位址.
  • CPSR

    寄存器用于設定CPU的工作模式,CPU有7種工作模式,具體可前往翻看

    v36.xx (工作模式篇) | cpu是韋小寶,有哪七個老婆?

    談論的使用者态(

    usr

    普通使用者)和核心态(

    sys

    超級使用者)對應的隻是其中的兩種.二者都共用相同的寄存器.還原它就是告訴CPU核心已切到普通使用者模式運作.
  • 其他寄存器沒有儲存的原因是系統調用不會用到它們,是以不需要儲存.
  • R7

    是在系統調用發生時用于記錄系統調用号,在信号處理過程中,R0将獲得信号編号,作為user.sighandle()的第一個參數.
  • count

    記錄是否儲存了信号上下文

OsArmA32SyscallHandle 系統調用總入口

/* The SYSCALL ID is in R7 on entry.  Parameters follow in R0..R6 */
/******************************************************************
由彙編調用,見于 los_hw_exc.s    / BLX    OsArmA32SyscallHandle
SYSCALL是産生系統調用時觸發的信号,R7寄存器存放具體的系統調用ID,也叫系統調用号
regs:參數就是所有寄存器
注意:本函數在使用者态和核心态下都可能被調用到
//MOV     R0, SP @擷取SP值,R0将作為OsArmA32SyscallHandle的參數
******************************************************************/
LITE_OS_SEC_TEXT UINT32 *OsArmA32SyscallHandle(UINT32 *regs)
{
    UINT32 ret;
    UINT8 nArgs;
    UINTPTR handle;
    UINT32 cmd = regs[REG_R7];//C7寄存器記錄了觸發了具體哪個系統調用
	
    if (cmd >= SYS_CALL_NUM) {//系統調用的總數
        PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
        return regs;
    }
	//使用者程序信号處理函數完成後的系統調用 svc 119 #__NR_sigreturn
    if (cmd == __NR_sigreturn) {
        OsRestorSignalContext(regs);//恢複信号上下文,回到使用者棧運作.
        return regs;
    }

    handle = g_syscallHandle[cmd];//拿到系統調用的注冊函數,類似 SysRead 
    nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
    nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);//擷取參數個數
    if ((handle == 0) || (nArgs > ARG_NUM_7)) {//系統調用必須有參數且參數不能大于8個
        PRINT_ERR("Unsupport syscall ID: %d nArgs: %d\n", cmd, nArgs);
        regs[REG_R0] = -ENOSYS;
        return regs;
    }
	//regs[0-6] 記錄系統調用的參數,這也是由R7寄存器儲存系統調用号的原因
    switch (nArgs) {//參數的個數 
        case ARG_NUM_0:
        case ARG_NUM_1:
            ret = (*(SyscallFun1)handle)(regs[REG_R0]);//執行系統調用,類似 SysUnlink(pathname);
            break;
        case ARG_NUM_2://如何是兩個參數的系統調用,這裡傳三個參數也沒有問題,因被調用函數不會去取用R2值
        case ARG_NUM_3:
            ret = (*(SyscallFun3)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2]);//類似 SysExecve(fileName, argv, envp);
            break;
        case ARG_NUM_4:
        case ARG_NUM_5:
            ret = (*(SyscallFun5)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2], regs[REG_R3],
                                         regs[REG_R4]);
            break;
        default:	//7個參數的情況
            ret = (*(SyscallFun7)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2], regs[REG_R3],
                                         regs[REG_R4], regs[REG_R5], regs[REG_R6]);
    }

    regs[REG_R0] = ret;//R0儲存系統調用傳回值
    OsSaveSignalContext(regs);//如果有信号要處理,将改寫pc,r0,r1寄存器,改變傳回正常使用者态路徑,而先去執行信号處理程式.

    /* Return the last value of curent_regs.  This supports context switches on return from the exception.
     * That capability is only used with the SYS_context_switch system call.
     */
    return regs;//傳回寄存器的值
}
           
  • 這是系統調用的總入口,所有的系統調用都要跑這裡要統一處理.通過系統号(儲存在R7),找到注冊函數并回調.完成系統調用過程.
  • 關于系統調用可檢視

    v37.xx (系統調用篇) | 系統調用到底經曆了什麼

    本篇不詳細說系統調用過程,隻說跟信号相關的部分.

  • OsArmA32SyscallHandle

    總體了解起來是被信号的儲存和還原兩個函數給包夾了.注意要在運作過程中去了解調用兩個函數的過程,對于同一個任務來說,一定是先執行

    OsSaveSignalContext

    ,第二次進入

    OsArmA32SyscallHandle

    後再執行

    OsRestorSignalContext

  • OsSaveSignalContext

    ,由它負責儲存user.source() 的上下文,其中改變了sp,r0/r1寄存器值,切到信号處理函數user.sighandle()運作.
  • 在函數的開頭,碰到系統調用号

    __NR_sigreturn

    ,直接恢複信号上下文就退出了,因為這是要切回user.source()繼續運作的操作.
//使用者程序信号處理函數完成後的系統調用 svc 119 #__NR_sigreturn
if (cmd == __NR_sigreturn) {
    OsRestorSignalContext(regs);//恢複信号上下文,回到使用者棧運作.
    return regs;
}
           

OsSaveSignalContext 儲存信号上下文

有了上面的鋪墊,就不難了解這個函數的作用.

/**********************************************
産生系統調用時,也就是軟中斷時,儲存使用者棧寄存器現場資訊
改寫PC寄存器的值
**********************************************/
void OsSaveSignalContext(unsigned int *sp)
{
    UINTPTR sigHandler;
    UINT32 intSave;
    LosTaskCB *task = NULL;
    LosProcessCB *process = NULL;
    sig_cb *sigcb = NULL;
    unsigned long cpsr;

    OS_RETURN_IF_VOID(sp == NULL);
    cpsr = OS_SYSCALL_GET_CPSR(sp);//擷取系統調用時的 CPSR值
    OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));//必須工作在CPU的使用者模式下,注意CPSR_USER_MODE(cpu層面)和OS_USER_MODE(系統層面)是兩碼事.
    SCHEDULER_LOCK(intSave);//如有不明白前往 https://my.oschina.net/weharmony 翻看工作模式/信号分發/信号處理篇
    task = OsCurrTaskGet();
    process = OsCurrProcessGet();
    sigcb = &task->sig;//擷取任務的信号控制塊
	//1.未儲存任務上下文任務
	//2.任何的信号标簽集不為空或者程序有信号要處理
    if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) {
        sigHandler = OsGetSigHandler();//擷取信号處理函數
        if (sigHandler == 0) {//信号沒有注冊
            sigcb->sigFlag = 0;
            process->sigShare = 0;
            SCHEDULER_UNLOCK(intSave);
            PRINT_ERR("The signal processing function for the current process pid =%d is NULL!\n", task->processID);
            return;
        }
        /* One pthread do the share signal */ 
        sigcb->sigFlag |= process->sigShare;//擴充任務的信号标簽集
        unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1;
        OsProcessExitCodeSignalSet(process, signo);//設定程序退出信号
        sigcb->context.CPSR = cpsr;		//儲存狀态寄存器
        sigcb->context.PC = sp[REG_PC]; //擷取被打斷現場寄存器的值
        sigcb->context.USP = sp[REG_SP];//使用者棧頂位置,以便能從核心棧切回使用者棧
        sigcb->context.ULR = sp[REG_LR];//使用者棧傳回位址
        sigcb->context.R0 = sp[REG_R0];	//系統調用的傳回值
        sigcb->context.R1 = sp[REG_R1];
        sigcb->context.R2 = sp[REG_R2];
        sigcb->context.R3 = sp[REG_R3]; 
        sigcb->context.R7 = sp[REG_R7];//為何參數不用傳R7,是因為系統調用發生時 R7始終儲存的是系統調用号.
        sigcb->context.R12 = sp[REG_R12];//詳見 https://my.oschina.net/weharmony/blog/4967613
        sp[REG_PC] = sigHandler;//指定信号執行函數,注意此處改變儲存任務上下文中PC寄存器的值,恢複上下文時将執行這個函數.
        sp[REG_R0] = signo;		//參數1,信号ID
        sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數2
        /* sig No bits 00000100 present sig No 3, but  1<< 3 = 00001000, so signo needs minus 1 */
        sigcb->sigFlag ^= 1ULL << (signo - 1);
        sigcb->context.count++;	//代表已儲存
    }
    SCHEDULER_UNLOCK(intSave);
}
           
  • 先是判斷執行條件,确實是有信号需要處理,有處理函數.自定義處理函數是由使用者程序安裝進來的,所有程序旗下的任務都共用,參數就是信号

    signo

    ,注意可不是系統調用号,有差別的.信号編号長這樣.
#define SIGHUP    1	//終端挂起或者控制程序終止
#define SIGINT    2	//鍵盤中斷(ctrl + c)
#define SIGQUIT   3	//鍵盤的退出鍵被按下
#define SIGILL    4	//非法指令
#define SIGTRAP   5	//跟蹤陷阱(trace trap),啟動程序,跟蹤代碼的執行
#define SIGABRT   6	//由abort(3)發出的退出指令
#define SIGIOT    SIGABRT //abort發出的信号
#define SIGBUS    7	//總線錯誤 
#define SIGFPE    8	//浮點異常
#define SIGKILL   9	//常用的指令 kill 9 123 | 不能被忽略、處理和阻塞
           

系統調用号長這樣,是不是看到一些很熟悉的函數.

#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
#define __NR_execve 11
#define __NR_chdir 12
#define __NR_time 13
#define __NR_mknod 14
#define __NR_chmod 15
#define __NR_lchown 16
#define __NR_break 17
           
  • 最後是最最最關鍵的代碼,改變pc寄存器的值,此值一變,在

    _osExceptSwiHdl

    中恢複上下文後,cpu跳到使用者空間的代碼段 user.sighandle(R0,R1) 開始執行,即執行信号處理函數.
sp[REG_PC] = sigHandler;//指定信号執行函數,注意此處改變儲存任務上下文中PC寄存器的值,恢複上下文時将執行這個函數.
sp[REG_R0] = signo;		//參數1,信号ID
sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數2
           

OsRestorSignalContext 恢複信号上下文

/****************************************************
恢複信号上下文,由系統調用之__NR_sigreturn産生,這是一個内部産生的系統調用.
為什麼要恢複呢?
因為系統調用的執行由任務核心态完成,使用的棧也是核心棧,CPU相關寄存器記錄的都是核心棧的内容,
而系統調用完成後,需傳回任務的使用者棧執行,這時需将CPU各寄存器回到使用者态現場
是以函數的功能就變成了還原寄存器的值
****************************************************/
void OsRestorSignalContext(unsigned int *sp)
{
    LosTaskCB *task = NULL; /* Do not adjust this statement */
    LosProcessCB *process = NULL;
    sig_cb *sigcb = NULL;
    UINT32 intSave;

    SCHEDULER_LOCK(intSave);
    task = OsCurrTaskGet();
    sigcb = &task->sig;//擷取目前任務信号控制塊

    if (sigcb->context.count != 1) {//必須之前儲存過,才能被恢複
        SCHEDULER_UNLOCK(intSave);
        PRINT_ERR("sig error count : %d\n", sigcb->context.count);
        return;
    }

    process = OsCurrProcessGet();//擷取目前程序
    sp[REG_PC] = sigcb->context.PC;//指令寄存器
    OS_SYSCALL_SET_CPSR(sp, sigcb->context.CPSR);//重置程式狀态寄存器
    sp[REG_SP] = sigcb->context.USP;//使用者棧堆棧指針, USP指的是 使用者态的堆棧,即将回到使用者棧繼續運作
    sp[REG_LR] = sigcb->context.ULR;//傳回使用者棧代碼執行位置
    sp[REG_R0] = sigcb->context.R0;
    sp[REG_R1] = sigcb->context.R1;
    sp[REG_R2] = sigcb->context.R2;
    sp[REG_R3] = sigcb->context.R3;
    sp[REG_R7] = sigcb->context.R7;
    sp[REG_R12] = sigcb->context.R12;
    sigcb->context.count--;	//信号上下文的數量回到減少
    process->sigShare = 0;	//回到使用者态,信号共享清0
    OsProcessExitCodeSignalClear(process);//清空程序退出碼
    SCHEDULER_UNLOCK(intSave);
}
           
  • 在信号處理函數完成之後,核心會觸發一個

    __NR_sigreturn

    的系統調用,又陷入核心态,回到了

    OsArmA32SyscallHandle

  • 恢複的過程很簡單,把之前儲存的信号上下文恢複到核心棧sp開始位置,資料在棧中的儲存順序可檢視 用棧方式篇 ,最重要的看這幾句.
sp[REG_PC] = sigcb->context.PC;//指令寄存器
sp[REG_SP] = sigcb->context.USP;//使用者棧堆棧指針, USP指的是 使用者态的堆棧,即将回到使用者棧繼續運作
sp[REG_LR] = sigcb->context.ULR;//傳回使用者棧代碼執行位置
           

注意這裡還不是真正的切換上下文,隻是改變核心棧中現有的資料.這些資料将還原給寄存器.

USP

ULR

指向的是使用者棧的位置.一旦

PC

USP

ULR

從棧中彈出賦給寄存器.才真正完成了核心棧到使用者棧的切換.回到了user.source()繼續運作.

  • 真正的切換彙編代碼如下,都已添加注釋,在儲存和恢複上下文中夾着

    OsArmA32SyscallHandle

@ Description: Software interrupt exception handler
_osExceptSwiHdl: @軟中斷異常處理,注意此時已在核心棧運作
@儲存任務上下文(TaskContext) 開始... 一定要對照TaskContext來了解
SUB     SP, SP, #(4 * 16)	@先申請16個棧空間單元用于處理本次軟中斷
STMIA   SP, {R0-R12}		@TaskContext.R[GEN_REGS_NUM] STMIA從左到右執行,先放R0 .. R12
MRS     R3, SPSR			@讀取本模式下的SPSR值
MOV     R4, LR				@儲存回跳寄存器LR

AND     R1, R3, #CPSR_MASK_MODE                          @ Interrupted mode 擷取中斷模式
CMP     R1, #CPSR_USER_MODE                              @ User mode	是否為使用者模式
BNE     OsKernelSVCHandler                               @ Branch if not user mode 非使用者模式下跳轉
@ 當為使用者模式時,擷取SP和LR寄出去值
@ we enter from user mode, we need get the values of  USER mode r13(sp) and r14(lr).
@ stmia with ^ will return the user mode registers (provided that r15 is not in the register list).
MOV     R0, SP											 @擷取SP值,R0将作為OsArmA32SyscallHandle的參數
STMFD   SP!, {R3}                                        @ Save the CPSR 入棧儲存CPSR值 => TaskContext.regPSR
ADD     R3, SP, #(4 * 17)                                @ Offset to pc/cpsr storage 跳到PC/CPSR存儲位置
STMFD   R3!, {R4}                                        @ Save the CPSR and r15(pc) 儲存LR寄存器 => TaskContext.PC
STMFD   R3, {R13, R14}^                                  @ Save user mode r13(sp) and r14(lr) 從右向左 儲存 => TaskContext.LR和SP
SUB     SP, SP, #4										 @ => TaskContext.resved
PUSH_FPU_REGS R1	@儲存中斷模式(使用者模式)											
@儲存任務上下文(TaskContext) 結束
MOV     FP, #0                                           @ Init frame pointer
CPSIE   I	@開中斷,表明在系統調用期間可響應中斷
BLX     OsArmA32SyscallHandle	/*交給C語言處理系統調用,參數為R0,指向TaskContext的開始位置*/
CPSID   I	@執行後續指令前必須先關中斷
@恢複任務上下文(TaskContext) 開始
POP_FPU_REGS R1											 @彈出FPU值給R1
ADD     SP, SP,#4										 @ 定位到儲存舊SPSR值的位置
LDMFD   SP!, {R3}                                        @ Fetch the return SPSR 彈出舊SPSR值
MSR     SPSR_cxsf, R3                                    @ Set the return mode SPSR 恢複該模式下的SPSR值

@ we are leaving to user mode, we need to restore the values of USER mode r13(sp) and r14(lr).
@ ldmia with ^ will return the user mode registers (provided that r15 is not in the register list)

LDMFD   SP!, {R0-R12}									 @恢複R0-R12寄存器
LDMFD   SP, {R13, R14}^                                  @ Restore user mode R13/R14 恢複使用者模式的R13/R14寄存器
ADD     SP, SP, #(2 * 4)								 @定位到儲存舊PC值的位置
LDMFD   SP!, {PC}^                                       @ Return to user 切回使用者模式運作
@恢複任務上下文(TaskContext) 結束
           

具體也可看這兩篇:

v42.xx (中斷切換篇) | 系統因中斷活力四射

v41.xx (任務切換篇) | 看彙編如何切換任務

百篇部落格分析.深挖核心地基

  • 給鴻蒙核心源碼加注釋過程中,整理出以下文章。内容立足源碼,常以生活場景打比方盡可能多的将核心知識點置入某種場景,具有畫面感,容易了解記憶。說别人能聽得懂的話很重要! 百篇部落格絕不是百度教條式的在說一堆诘屈聱牙的概念,那沒什麼意思。更希望讓核心變得栩栩如生,倍感親切.确實有難度,自不量力,但已經出發,回頭已是不可能的了。 😛
  • 與代碼有bug需不斷debug一樣,文章和注解内容會存在不少錯漏之處,請多包涵,但會反複修正,持續更新,v**.xx 代表文章序号和修改的次數,精雕細琢,言簡意赅,力求打造精品内容。

按功能子產品:

基礎工具 加載運作 程序管理 編譯建構

雙向連結清單

位圖管理

用棧方式

定時器

原子操作

時間管理

ELF格式

ELF解析

靜态連結

重定位

程序映像

程序概念

Fork

特殊程序

程序回收

信号生産

Shell編輯

Shell解析

編譯環境

編譯過程

環境腳本

建構工具

gn應用

忍者ninja

程序通訊 記憶體管理 前因後果 任務管理

自旋鎖

互斥鎖

信号量

事件控制

消息隊列

記憶體配置設定

記憶體彙編

記憶體映射

記憶體規則

實體記憶體

總目錄

排程故事

記憶體主奴

源碼注釋

源碼結構

靜态站點

時鐘任務

任務排程

排程隊列

排程機制

線程概念

并發并行

CPU

系統調用

任務切換

檔案系統 硬體架構

檔案概念

索引節點

挂載目錄

根檔案系統

字元裝置

VFS

檔案句柄

管道檔案

彙編基礎

彙編傳參

工作模式

寄存器

異常接管

彙編彙總

中斷切換

中斷概念

中斷管理

百萬漢字注解.精讀核心源碼

四大碼倉中文注解 . 定期同步官方代碼

v49.03 鴻蒙核心源碼分析(信号消費篇) | 誰讓CPU連續四次換棧運作 | 百篇部落格分析OpenHarmony源碼

鴻蒙研究站( weharmonyos ) | 每天死磕一點點,原創不易,歡迎轉載,請注明出處。若能支援點贊更好,感謝每一份支援。

繼續閱讀