99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鴻蒙內(nèi)核源碼分析:用戶棧和內(nèi)核棧的兩次切換

鴻蒙系統(tǒng)HarmonyOS ? 來源:my.oschina ? 作者:鴻蒙內(nèi)核源碼分析 ? 2021-04-23 17:17 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本篇有相當?shù)碾y度,涉及用戶棧和內(nèi)核棧的兩次切換,寄存器改值,將圍繞下圖來說明.

o4YBAGCCj9eAPgtXAAG0VYDhnWo613.png

解讀

為本篇理解方便,把圖做簡化標簽說明:

user:用戶空間

kernel:內(nèi)核空間

source(...):源函數(shù)

sighandle(...):信號處理函數(shù),

syscall(...):系統(tǒng)調(diào)用,參數(shù)為系統(tǒng)調(diào)用號,如sigreturn,N(表任意)

user.source():表示在用戶空間運行的源函數(shù)

系列篇已多次說過,用戶態(tài)的任務(wù)有兩個運行棧,一個是用戶棧,一個是內(nèi)核棧.??臻g分別來自用戶空間和內(nèi)核空間.兩種空間是有嚴格的地址劃分的,通過虛擬地址的大小就能判斷出是用戶空間還是內(nèi)核空間.系統(tǒng)調(diào)用本質(zhì)上是軟中斷,它使CPU執(zhí)行指令的場地由用戶棧變成內(nèi)核棧.怎么變的并不復雜,就是改變(sp和cpsr寄存器的值).sp指向哪個棧就代表在哪個棧運行, 當cpu在用戶棧運行時是不能訪問內(nèi)核空間的,但內(nèi)核態(tài)任務(wù)可以訪問整個空間,而且內(nèi)核態(tài)任務(wù)沒有用戶棧.

理解了上面的說明,再來說下正常系統(tǒng)調(diào)用流程是這樣的: user.source() -> kernel.syscall(N) - > user.source() ,想要回到user.source()繼續(xù)運行,就必須保存用戶?,F(xiàn)場各寄存器的值.這些值保存在內(nèi)核棧中,恢復也是從內(nèi)核?;謴?

信號消費的過程的上圖可簡化表示為: user.source() -> kernel.syscall(N) ->user.sighandle() ->kernel.syscall(sigreturn) -> user.source() 在原本要回到user.source()的中間插入了信號處理函數(shù)的調(diào)用. 這正是本篇要通過代碼來說清楚的核心問題.

順著這個思路可以推到以下幾點,實際也是這么做的:

kernel.syscall(N) 中必須要再次保存user.source()的上下文sig_switch_context,為何已經(jīng)保存了一次還要再保存一次?

因為第一次是保存在內(nèi)核棧中,而內(nèi)核棧這部分數(shù)據(jù)會因回到用戶態(tài)user.sighandle()運行而被恢復現(xiàn)場出棧了.保存現(xiàn)場/恢復現(xiàn)場是成雙出隊的好基友,注意有些文章說會把整個內(nèi)核棧清空,這是不對的.

第二次保存在任務(wù)結(jié)構(gòu)體中,任務(wù)來源于任務(wù)池,是內(nèi)核全局變量,常駐內(nèi)存的.兩次保存的都是user.source()運行時現(xiàn)場信息,再回顧下相關(guān)的結(jié)構(gòu)體.關(guān)鍵是sig_switch_context

typedef struct {
    // ...
    sig_cb  sig;//信號控制塊,用于異步通信
} LosTaskCB;
typedef struct {//信號控制塊(描述符)
    sigset_t sigFlag;		//不屏蔽的信號集
    sigset_t sigPendFlag;	//信號阻塞標簽集,記錄那些信號來過,任務(wù)依然阻塞的集合.即:這些信號不能喚醒任務(wù)
    sigset_t sigprocmask; /* Signals that are blocked            */	//任務(wù)屏蔽了哪些信號
    sq_queue_t sigactionq;	//信號捕捉隊列					
    LOS_DL_LIST waitList;	//等待鏈表,上面掛的是等待信號到來的任務(wù), 請查找 OsTaskWait(&sigcb->waitList, timeout, TRUE)	理解						
    sigset_t sigwaitmask; /* Waiting for pending signals         */	//任務(wù)在等待哪些信號的到來
    siginfo_t sigunbinfo; /* Signal info when task unblocked     */	//任務(wù)解鎖時的信號信息
    sig_switch_context context;	//信號切換上下文, 用于保存切換現(xiàn)場, 比如發(fā)生系統(tǒng)調(diào)用時的返回,涉及同一個任務(wù)的兩個棧進行切換			
} sig_cb;

還必須要改變原有PC/R0/R1寄存器的值.想要執(zhí)行user.sighandle(),PC寄存器就必須指向它,而R0,R1就是它的參數(shù).

信號處理完成后須回到內(nèi)核態(tài),怎么再次陷入內(nèi)核態(tài)? 答案是:__NR_sigreturn,這也是個系統(tǒng)調(diào)用.回來后還原sig_switch_context,即還原user.source()被打斷時SP/PC等寄存器的值,使其跳回到用戶棧從user.source()的被打斷處繼續(xù)執(zhí)行.

有了這三個推論,再理解下面的代碼就是吹灰之力了,涉及三個關(guān)鍵函數(shù)OsArmA32SyscallHandle,OsSaveSignalContext,OsRestorSignalContext本篇一一解讀,徹底挖透.先看信號上下文結(jié)構(gòu)體sig_switch_context.

sig_switch_context

//任務(wù)中斷上下文
#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;	//存放系統(tǒng)調(diào)用的ID
    unsigned int count;	//記錄是否保存了信號上下文
} sig_switch_context;

保存user.source()現(xiàn)場的結(jié)構(gòu)體,USP,ULR代表用戶棧指針和返回地址.

CPSR寄存器用于設(shè)置CPU的工作模式,CPU有7種工作模式,具體可前往翻看
v36.xx (工作模式篇) | cpu是韋小寶,有哪七個老婆?談?wù)摰挠脩魬B(tài)(usr普通用戶)和內(nèi)核態(tài)(sys超級用戶)對應(yīng)的只是其中的兩種.二者都共用相同的寄存器.還原它就是告訴CPU內(nèi)核已切到普通用戶模式運行.

其他寄存器沒有保存的原因是系統(tǒng)調(diào)用不會用到它們,所以不需要保存.

R7是在系統(tǒng)調(diào)用發(fā)生時用于記錄系統(tǒng)調(diào)用號,在信號處理過程中,R0將獲得信號編號,作為user.sighandle()的第一個參數(shù).

count記錄是否保存了信號上下文

OsArmA32SyscallHandle 系統(tǒng)調(diào)用總?cè)肟?/p>

/* The SYSCALL ID is in R7 on entry.  Parameters follow in R0..R6 */
/******************************************************************
由匯編調(diào)用,見于 los_hw_exc.s    / BLX    OsArmA32SyscallHandle
SYSCALL是產(chǎn)生系統(tǒng)調(diào)用時觸發(fā)的信號,R7寄存器存放具體的系統(tǒng)調(diào)用ID,也叫系統(tǒng)調(diào)用號
regs:參數(shù)就是所有寄存器
注意:本函數(shù)在用戶態(tài)和內(nèi)核態(tài)下都可能被調(diào)用到
//MOV     R0, SP @獲取SP值,R0將作為OsArmA32SyscallHandle的參數(shù)
******************************************************************/
LITE_OS_SEC_TEXT UINT32 *OsArmA32SyscallHandle(UINT32 *regs)
{
    UINT32 ret;
    UINT8 nArgs;
    UINTPTR handle;
    UINT32 cmd = regs[REG_R7];//C7寄存器記錄了觸發(fā)了具體哪個系統(tǒng)調(diào)用
	
    if (cmd >= SYS_CALL_NUM) {//系統(tǒng)調(diào)用的總數(shù)
        PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
        return regs;
    }
	//用戶進程信號處理函數(shù)完成后的系統(tǒng)調(diào)用 svc 119 #__NR_sigreturn
    if (cmd == __NR_sigreturn) {
        OsRestorSignalContext(regs);//恢復信號上下文,回到用戶棧運行.
        return regs;
    }

    handle = g_syscallHandle[cmd];//拿到系統(tǒng)調(diào)用的注冊函數(shù),類似 SysRead 
    nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
    nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);//獲取參數(shù)個數(shù)
    if ((handle == 0) || (nArgs > ARG_NUM_7)) {//系統(tǒng)調(diào)用必須有參數(shù)且參數(shù)不能大于8個
        PRINT_ERR("Unsupport syscall ID: %d nArgs: %d\n", cmd, nArgs);
        regs[REG_R0] = -ENOSYS;
        return regs;
    }
	//regs[0-6] 記錄系統(tǒng)調(diào)用的參數(shù),這也是由R7寄存器保存系統(tǒng)調(diào)用號的原因
    switch (nArgs) {//參數(shù)的個數(shù) 
        case ARG_NUM_0:
        case ARG_NUM_1:
            ret = (*(SyscallFun1)handle)(regs[REG_R0]);//執(zhí)行系統(tǒng)調(diào)用,類似 SysUnlink(pathname);
            break;
        case ARG_NUM_2://如何是兩個參數(shù)的系統(tǒng)調(diào)用,這里傳三個參數(shù)也沒有問題,因被調(diào)用函數(shù)不會去取用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個參數(shù)的情況
            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保存系統(tǒng)調(diào)用返回值
    OsSaveSignalContext(regs);//如果有信號要處理,將改寫pc,r0,r1寄存器,改變返回正常用戶態(tài)路徑,而先去執(zhí)行信號處理程序.

    /* 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;//返回寄存器的值
}

解讀

這是系統(tǒng)調(diào)用的總?cè)肟?所有的系統(tǒng)調(diào)用都要跑這里要統(tǒng)一處理.通過系統(tǒng)號(保存在R7),找到注冊函數(shù)并回調(diào).完成系統(tǒng)調(diào)用過程.

關(guān)于系統(tǒng)調(diào)用可查看v37.xx (系統(tǒng)調(diào)用篇) | 系統(tǒng)調(diào)用到底經(jīng)歷了什么本篇不詳細說系統(tǒng)調(diào)用過程,只說跟信號相關(guān)的部分.

OsArmA32SyscallHandle總體理解起來是被信號的保存和還原兩個函數(shù)給包夾了.注意要在運行過程中去理解調(diào)用兩個函數(shù)的過程,對于同一個任務(wù)來說,一定是先執(zhí)行OsSaveSignalContext,第二次進入OsArmA32SyscallHandle后再執(zhí)行OsRestorSignalContext.

看OsSaveSignalContext,由它負責保存user.source() 的上下文,其中改變了sp,r0/r1寄存器值,切到信號處理函數(shù)user.sighandle()運行.

在函數(shù)的開頭,碰到系統(tǒng)調(diào)用號__NR_sigreturn,直接恢復信號上下文就退出了,因為這是要切回user.source()繼續(xù)運行的操作.

	//用戶進程信號處理函數(shù)完成后的系統(tǒng)調(diào)用 svc 119 #__NR_sigreturn
  if (cmd == __NR_sigreturn) {
      OsRestorSignalContext(regs);//恢復信號上下文,回到用戶棧運行.
      return regs;
  }

OsSaveSignalContext 保存信號上下文

有了上面的鋪墊,就不難理解這個函數(shù)的作用.

/**********************************************
產(chǎn)生系統(tǒng)調(diào)用時,也就是軟中斷時,保存用戶棧寄存器現(xiàn)場信息
改寫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);//獲取系統(tǒng)調(diào)用時的 CPSR值
    OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));//必須工作在CPU的用戶模式下,注意CPSR_USER_MODE(cpu層面)和OS_USER_MODE(系統(tǒng)層面)是兩碼事.
    SCHEDULER_LOCK(intSave);//如有不明白前往 https://my.oschina.net/weharmony 翻看工作模式/信號分發(fā)/信號處理篇
    task = OsCurrTaskGet();
    process = OsCurrProcessGet();
    sigcb = &task->sig;//獲取任務(wù)的信號控制塊
	//1.未保存任務(wù)上下文任務(wù)
	//2.任何的信號標簽集不為空或者進程有信號要處理
    if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) {
        sigHandler = OsGetSigHandler();//獲取信號處理函數(shù)
        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;//擴展任務(wù)的信號標簽集
        unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1;
        OsProcessExitCodeSignalSet(process, signo);//設(shè)置進程退出信號
        sigcb->context.CPSR = cpsr;		//保存狀態(tài)寄存器
        sigcb->context.PC = sp[REG_PC]; //獲取被打斷現(xiàn)場寄存器的值
        sigcb->context.USP = sp[REG_SP];//用戶棧頂位置,以便能從內(nèi)核棧切回用戶棧
        sigcb->context.ULR = sp[REG_LR];//用戶棧返回地址
        sigcb->context.R0 = sp[REG_R0];	//系統(tǒng)調(diào)用的返回值
        sigcb->context.R1 = sp[REG_R1];
        sigcb->context.R2 = sp[REG_R2];
        sigcb->context.R3 = sp[REG_R3]; 
        sigcb->context.R7 = sp[REG_R7];//為何參數(shù)不用傳R7,是因為系統(tǒng)調(diào)用發(fā)生時 R7始終保存的是系統(tǒng)調(diào)用號.
        sigcb->context.R12 = sp[REG_R12];//詳見 https://my.oschina.net/weharmony/blog/4967613
        sp[REG_PC] = sigHandler;//指定信號執(zhí)行函數(shù),注意此處改變保存任務(wù)上下文中PC寄存器的值,恢復上下文時將執(zhí)行這個函數(shù).
        sp[REG_R0] = signo;		//參數(shù)1,信號ID
        sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數(shù)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);
}

解讀

先是判斷執(zhí)行條件,確實是有信號需要處理,有處理函數(shù).自定義處理函數(shù)是由用戶進程安裝進來的,所有進程旗下的任務(wù)都共用,參數(shù)就是信號signo,注意可不是系統(tǒng)調(diào)用號,有區(qū)別的.信號編號長這樣.

  #define SIGHUP    1	//終端掛起或者控制進程終止
  #define SIGINT    2	//鍵盤中斷(ctrl + c)
  #define SIGQUIT   3	//鍵盤的退出鍵被按下
  #define SIGILL    4	//非法指令
  #define SIGTRAP   5	//跟蹤陷阱(trace trap),啟動進程,跟蹤代碼的執(zhí)行
  #define SIGABRT   6	//由abort(3)發(fā)出的退出指令
  #define SIGIOT    SIGABRT //abort發(fā)出的信號
  #define SIGBUS    7	//總線錯誤 
  #define SIGFPE    8	//浮點異常
  #define SIGKILL   9	//常用的命令 kill 9 123 | 不能被忽略、處理和阻塞
系統(tǒng)調(diào)用號長這樣,是不是看到一些很熟悉的函數(shù).
  #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

最后是最最最關(guān)鍵的代碼,改變pc寄存器的值,此值一變,在_osExceptSwiHdl中恢復上下文后,cpu跳到用戶空間的代碼段 user.sighandle(R0,R1) 開始執(zhí)行,即執(zhí)行信號處理函數(shù).

  sp[REG_PC] = sigHandler;//指定信號執(zhí)行函數(shù),注意此處改變保存任務(wù)上下文中PC寄存器的值,恢復上下文時將執(zhí)行這個函數(shù).
  sp[REG_R0] = signo;		//參數(shù)1,信號ID
  sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數(shù)2

OsRestorSignalContext 恢復信號上下文

/****************************************************
恢復信號上下文,由系統(tǒng)調(diào)用之__NR_sigreturn產(chǎn)生,這是一個內(nèi)部產(chǎn)生的系統(tǒng)調(diào)用.
為什么要恢復呢?
因為系統(tǒng)調(diào)用的執(zhí)行由任務(wù)內(nèi)核態(tài)完成,使用的棧也是內(nèi)核棧,CPU相關(guān)寄存器記錄的都是內(nèi)核棧的內(nèi)容,
而系統(tǒng)調(diào)用完成后,需返回任務(wù)的用戶棧執(zhí)行,這時需將CPU各寄存器回到用戶態(tài)現(xiàn)場
所以函數(shù)的功能就變成了還原寄存器的值
****************************************************/
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;//獲取當前任務(wù)信號控制塊

    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);//重置程序狀態(tài)寄存器
    sp[REG_SP] = sigcb->context.USP;//用戶棧堆棧指針, USP指的是 用戶態(tài)的堆棧,即將回到用戶棧繼續(xù)運行
    sp[REG_LR] = sigcb->context.ULR;//返回用戶棧代碼執(zhí)行位置
    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--;	//信號上下文的數(shù)量回到減少
    process->sigShare = 0;	//回到用戶態(tài),信號共享清0
    OsProcessExitCodeSignalClear(process);//清空進程退出碼
    SCHEDULER_UNLOCK(intSave);
}

解讀

在信號處理函數(shù)完成之后,內(nèi)核會觸發(fā)一個__NR_sigreturn的系統(tǒng)調(diào)用,又陷入內(nèi)核態(tài),回到了OsArmA32SyscallHandle.

恢復的過程很簡單,把之前保存的信號上下文恢復到內(nèi)核棧sp開始位置,數(shù)據(jù)在棧中的保存順序可查看 用棧方式篇 ,最重要的看這幾句.

  sp[REG_PC] = sigcb->context.PC;//指令寄存器
  sp[REG_SP] = sigcb->context.USP;//用戶棧堆棧指針, USP指的是 用戶態(tài)的堆棧,即將回到用戶棧繼續(xù)運行
  sp[REG_LR] = sigcb->context.ULR;//返回用戶棧代碼執(zhí)行位置

注意這里還不是真正的切換上下文,只是改變內(nèi)核棧中現(xiàn)有的數(shù)據(jù).這些數(shù)據(jù)將還原給寄存器.USP和ULR指向的是用戶棧的位置.一旦PC,USP,ULR從棧中彈出賦給寄存器.才真正完成了內(nèi)核棧到用戶棧的切換.回到了user.source()繼續(xù)運行.

真正的切換匯編代碼如下,都已添加注釋,在保存和恢復上下文中夾著OsArmA32SyscallHandle

@ Description: Software interrupt exception handler
_osExceptSwiHdl: @軟中斷異常處理,注意此時已在內(nèi)核棧運行
	@保存任務(wù)上下文(TaskContext) 開始... 一定要對照TaskContext來理解
SUB     SP, SP, #(4 * 16)	@先申請16個棧空間單元用于處理本次軟中斷
STMIA   SP, {R0-R12}		@TaskContext.R[GEN_REGS_NUM] STMIA從左到右執(zhí)行,先放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 非用戶模式下跳轉(zhuǎn)
	@ 當為用戶模式時,獲取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的參數(shù)
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	@保存中斷模式(用戶模式)											
	@保存任務(wù)上下文(TaskContext) 結(jié)束
MOV     FP, #0                                           @ Init frame pointer
CPSIE   I	@開中斷,表明在系統(tǒng)調(diào)用期間可響應(yīng)中斷
BLX     OsArmA32SyscallHandle	/*交給C語言處理系統(tǒng)調(diào)用,參數(shù)為R0,指向TaskContext的開始位置*/
CPSID   I	@執(zhí)行后續(xù)指令前必須先關(guān)中斷
	@恢復任務(wù)上下文(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 切回用戶模式運行
	@恢復任務(wù)上下文(TaskContext) 結(jié)束

編輯:hfy

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5434

    瀏覽量

    124455
  • 信號處理
    +關(guān)注

    關(guān)注

    48

    文章

    1056

    瀏覽量

    104099
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    鴻蒙內(nèi)核源碼分析:用通俗易懂的語言告訴你鴻蒙內(nèi)核發(fā)生了什么?

    鴻蒙虛擬內(nèi)存全景圖圖來自 鴻蒙內(nèi)核源碼注釋中文版 【Gitee倉 】再看鴻蒙用戶空間全景圖圖來
    發(fā)表于 11-19 10:14

    鴻蒙內(nèi)核源碼分析(調(diào)度機制篇):Task是如何被調(diào)度執(zhí)行的

    系列篇內(nèi)存中查看是最重要的,切換任務(wù)上下文,參數(shù)是新老個任務(wù),一個要保存現(xiàn)場,一個要恢復現(xiàn)場。什么是任務(wù)上下文?看鴻蒙系統(tǒng)源碼分析(總目錄
    發(fā)表于 11-23 10:53

    鴻蒙內(nèi)核源碼分析(雙循環(huán)鏈表篇) :內(nèi)核最重要結(jié)構(gòu)體

    鴻蒙源碼分析系列文章圖解鴻蒙內(nèi)核, 從 HarmonyOS 架構(gòu)層視角整理成文, 并首創(chuàng)用生活場景講故事的方式試圖去解構(gòu)
    發(fā)表于 11-24 13:39

    鴻蒙內(nèi)核源碼分析(雙循環(huán)鏈表篇) :內(nèi)核最重要結(jié)構(gòu)體

    內(nèi)核中無處不在,在整個內(nèi)核占了極大的比重,豪不夸張的說理解LOS_DL_LIST及相關(guān)函數(shù)是讀懂鴻蒙內(nèi)核的關(guān)鍵。前后指針就像人的只左右手
    發(fā)表于 11-26 08:00

    鴻蒙內(nèi)核源碼分析(雙循環(huán)鏈表篇) :內(nèi)核最重要結(jié)構(gòu)體

    內(nèi)核中無處不在,在整個內(nèi)核占了極大的比重,豪不夸張的說理解LOS_DL_LIST及相關(guān)函數(shù)是讀懂鴻蒙內(nèi)核的關(guān)鍵。前后指針就像人的只左右手
    發(fā)表于 12-02 16:55

    操作系統(tǒng)為什么分內(nèi)核態(tài)和用戶態(tài)?這者如何切換?

    操作系統(tǒng)為什么分內(nèi)核態(tài)和用戶態(tài),這者如何切換?進程在地址空間會劃分為哪些區(qū)域?堆和有什么區(qū)別?
    發(fā)表于 07-23 09:01

    鴻蒙內(nèi)核源碼分析(百篇博客分析.挖透鴻蒙內(nèi)核)

    致敬內(nèi)核開發(fā)者感謝開放原子開源基金會,致敬鴻蒙內(nèi)核開發(fā)者??梢院敛豢鋸埖恼f鴻蒙內(nèi)核源碼可作為大學
    發(fā)表于 07-04 17:16

    一文詳解Linux內(nèi)核回溯與妙用

    網(wǎng)上或多或少都能找到回溯的一些文章,但是講的都并不完整,沒有將內(nèi)核回溯的功能用于實際的內(nèi)核、應(yīng)用程序調(diào)試,這是本篇文章的核心:盡可能引導讀者將
    的頭像 發(fā)表于 10-05 10:02 ?5806次閱讀
    一文詳解Linux<b class='flag-5'>內(nèi)核</b>的<b class='flag-5'>棧</b>回溯與妙用

    對Linux的進程內(nèi)核的認識

    在每一個進程的生命周期中,必然會通過到系統(tǒng)調(diào)用陷入內(nèi)核。在執(zhí)行系統(tǒng)調(diào)用陷入內(nèi)核之后,這些內(nèi)核代碼所使用的并不是原先用戶空間中的
    發(fā)表于 05-12 08:53 ?743次閱讀
    對Linux的進程<b class='flag-5'>內(nèi)核</b><b class='flag-5'>棧</b>的認識

    淺談鴻蒙內(nèi)核源碼的CPU四,寄存器改值

    本篇有相當?shù)碾y度,涉及用戶內(nèi)核切換,CPU四
    的頭像 發(fā)表于 04-28 16:56 ?1848次閱讀
    淺談<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>的CPU四<b class='flag-5'>次</b>換<b class='flag-5'>棧</b>,寄存器改值

    淺談鴻蒙內(nèi)核源碼

    上面的代碼和鴻蒙內(nèi)核方式一樣,都采用了遞減滿的方式, 什么是遞減滿?
    的頭像 發(fā)表于 04-24 11:21 ?1695次閱讀
    淺談<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>的<b class='flag-5'>棧</b>

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

    為何鴻蒙內(nèi)核源碼分析系列開篇就說 LOS_DL_LIST ? 因為它在鴻蒙 LOS 內(nèi)核中無處
    發(fā)表于 11-24 17:54 ?35次下載
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b><b class='flag-5'>分析</b> :<b class='flag-5'>內(nèi)核</b>最重要結(jié)構(gòu)體

    華為鴻蒙系統(tǒng)內(nèi)核源碼分析上冊

    鴻蒙內(nèi)核源碼注釋中文版【 Gitee倉】給 Harmoηy○S源碼逐行加上中文注解,詳細闡述設(shè)計細節(jié),助你快速精讀 Harmonyos內(nèi)核源碼
    發(fā)表于 04-09 14:40 ?17次下載

    系統(tǒng)調(diào)用:用戶內(nèi)核切換(上)

    當發(fā)生系統(tǒng)調(diào)用、產(chǎn)生異常,外設(shè)發(fā)生中斷等事件時,會發(fā)生用戶內(nèi)核之間的切換, 本文從系統(tǒng)調(diào)用角度分析
    的頭像 發(fā)表于 07-31 11:27 ?1266次閱讀
    系統(tǒng)調(diào)用:<b class='flag-5'>用戶</b><b class='flag-5'>棧</b>與<b class='flag-5'>內(nèi)核</b><b class='flag-5'>棧</b>的<b class='flag-5'>切換</b>(上)

    歡創(chuàng)播報 華為宣布鴻蒙內(nèi)核已超越Linux內(nèi)核

    自誕生以來的最大一升級換代。HarmonyOS NEXT不依賴傳統(tǒng)的Unix內(nèi)核和Linux內(nèi)核,而是依靠自主的鴻蒙內(nèi)核。這就意味著,
    的頭像 發(fā)表于 06-27 11:30 ?1279次閱讀