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

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

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

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

鴻蒙系統(tǒng)HarmonyOS ? 來源:my.oschina ? 作者:鴻蒙內(nèi)核源碼分析 ? 2021-04-28 16:56 ? 次閱讀

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

o4YBAGCJIoaASEOiAAFXah8_bJQ641.png

解讀

為本篇理解方便,把圖做簡(jiǎn)化標(biāo)簽說明:

user:用戶空間

kernel:內(nèi)核空間

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

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

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

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

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

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

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

順著這個(gè)思路可以推到以下幾點(diǎn),實(shí)際也是這么做的:

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

因?yàn)榈谝淮问潜4嬖趦?nèi)核棧中,而內(nèi)核棧這部分?jǐn)?shù)據(jù)會(huì)因回到用戶態(tài)user.sighandle()運(yùn)行而被恢復(fù)現(xiàn)場(chǎng)出棧了.保存現(xiàn)場(chǎng)/恢復(fù)現(xiàn)場(chǎng)是成雙出隊(duì)的好基友,注意有些文章說會(huì)把整個(gè)內(nèi)核棧清空,這是不對(duì)的.

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

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

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

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

有了這三個(gè)推論,再理解下面的代碼就是吹灰之力了,涉及三個(gè)關(guān)鍵函數(shù)OsArmA32SyscallHandle,OsSaveSignalContext,OsRestorSignalContext本篇一一解讀,徹底挖透.先看信號(hào)上下文結(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 {//信號(hào)切換上下文
    TASK_IRQ_CONTEXT
    unsigned int R7;	//存放系統(tǒng)調(diào)用的ID
    unsigned int count;	//記錄是否保存了信號(hào)上下文
} sig_switch_context;

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

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

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

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

count記錄是否保存了信號(hào)上下文

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)用時(shí)觸發(fā)的信號(hào),R7寄存器存放具體的系統(tǒng)調(diào)用ID,也叫系統(tǒng)調(diào)用號(hà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ā)了具體哪個(gè)系統(tǒng)調(diào)用
	
    if (cmd >= SYS_CALL_NUM) {//系統(tǒng)調(diào)用的總數(shù)
        PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
        return regs;
    }
	//用戶進(jìn)程信號(hào)處理函數(shù)完成后的系統(tǒng)調(diào)用 svc 119 #__NR_sigreturn
    if (cmd == __NR_sigreturn) {
        OsRestorSignalContext(regs);//恢復(fù)信號(hào)上下文,回到用戶棧運(yùn)行.
        return regs;
    }

    handle = g_syscallHandle[cmd];//拿到系統(tǒng)調(diào)用的注冊(cè)函數(shù),類似 SysRead 
    nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
    nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);//獲取參數(shù)個(gè)數(shù)
    if ((handle == 0) || (nArgs > ARG_NUM_7)) {//系統(tǒng)調(diào)用必須有參數(shù)且參數(shù)不能大于8個(gè)
        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)用號(hào)的原因
    switch (nArgs) {//參數(shù)的個(gè)數(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://如何是兩個(gè)參數(shù)的系統(tǒng)調(diào)用,這里傳三個(gè)參數(shù)也沒有問題,因被調(diào)用函數(shù)不會(huì)去取用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個(gè)參數(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);//如果有信號(hào)要處理,將改寫pc,r0,r1寄存器,改變返回正常用戶態(tài)路徑,而先去執(zhí)行信號(hào)處理程序.

    /* 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)號(hào)(保存在R7),找到注冊(cè)函數(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)歷了什么本篇不詳細(xì)說系統(tǒng)調(diào)用過程,只說跟信號(hào)相關(guān)的部分.

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

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

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

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

OsSaveSignalContext 保存信號(hào)上下文

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

/**********************************************
產(chǎn)生系統(tǒng)調(diào)用時(shí),也就是軟中斷時(shí),保存用戶棧寄存器現(xiàn)場(chǎng)信息
改寫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)用時(shí)的 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 翻看工作模式/信號(hào)分發(fā)/信號(hào)處理篇
    task = OsCurrTaskGet();
    process = OsCurrProcessGet();
    sigcb = &task->sig;//獲取任務(wù)的信號(hào)控制塊
	//1.未保存任務(wù)上下文任務(wù)
	//2.任何的信號(hào)標(biāo)簽集不為空或者進(jìn)程有信號(hào)要處理
    if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) {
        sigHandler = OsGetSigHandler();//獲取信號(hào)處理函數(shù)
        if (sigHandler == 0) {//信號(hào)沒有注冊(cè)
            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;//擴(kuò)展任務(wù)的信號(hào)標(biāo)簽集
        unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1;
        OsProcessExitCodeSignalSet(process, signo);//設(shè)置進(jìn)程退出信號(hào)
        sigcb->context.CPSR = cpsr;		//保存狀態(tài)寄存器
        sigcb->context.PC = sp[REG_PC]; //獲取被打斷現(xiàn)場(chǎng)寄存器的值
        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,是因?yàn)橄到y(tǒng)調(diào)用發(fā)生時(shí) R7始終保存的是系統(tǒng)調(diào)用號(hào).
        sigcb->context.R12 = sp[REG_R12];//詳見 https://my.oschina.net/weharmony/blog/4967613
        sp[REG_PC] = sigHandler;//指定信號(hào)執(zhí)行函數(shù),注意此處改變保存任務(wù)上下文中PC寄存器的值,恢復(fù)上下文時(shí)將執(zhí)行這個(gè)函數(shù).
        sp[REG_R0] = signo;		//參數(shù)1,信號(hào)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í)是有信號(hào)需要處理,有處理函數(shù).自定義處理函數(shù)是由用戶進(jìn)程安裝進(jìn)來的,所有進(jìn)程旗下的任務(wù)都共用,參數(shù)就是信號(hào)signo,注意可不是系統(tǒng)調(diào)用號(hào),有區(qū)別的.信號(hào)編號(hào)長(zhǎng)這樣.

#define SIGHUP    1	//終端掛起或者控制進(jìn)程終止
#define SIGINT    2	//鍵盤中斷(ctrl + c)
#define SIGQUIT   3	//鍵盤的退出鍵被按下
#define SIGILL    4	//非法指令
#define SIGTRAP   5	//跟蹤陷阱(trace trap),啟動(dòng)進(jìn)程,跟蹤代碼的執(zhí)行
#define SIGABRT   6	//由abort(3)發(fā)出的退出指令
#define SIGIOT    SIGABRT //abort發(fā)出的信號(hào)
#define SIGBUS    7	//總線錯(cuò)誤 
#define SIGFPE    8	//浮點(diǎn)異常
#define SIGKILL   9	//常用的命令 kill 9 123 | 不能被忽略、處理和阻塞

系統(tǒng)調(diào)用號(hào)長(zhǎng)這樣,是不是看到一些很熟悉的函數(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中恢復(fù)上下文后,cpu跳到用戶空間的代碼段 user.sighandle(R0,R1) 開始執(zhí)行,即執(zhí)行信號(hào)處理函數(shù).

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

OsRestorSignalContext 恢復(fù)信號(hào)上下文

/****************************************************
恢復(fù)信號(hào)上下文,由系統(tǒng)調(diào)用之__NR_sigreturn產(chǎn)生,這是一個(gè)內(nèi)部產(chǎn)生的系統(tǒng)調(diào)用.
為什么要恢復(fù)呢?
因?yàn)橄到y(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í)行,這時(shí)需將CPU各寄存器回到用戶態(tài)現(xiàn)場(chǎng)
所以函數(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;//獲取當(dāng)前任務(wù)信號(hào)控制塊

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

    process = OsCurrProcessGet();//獲取當(dāng)前進(jìn)程
    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ù)運(yùn)行
    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--;	//信號(hào)上下文的數(shù)量回到減少
    process->sigShare = 0;	//回到用戶態(tài),信號(hào)共享清0
    OsProcessExitCodeSignalClear(process);//清空進(jìn)程退出碼
    SCHEDULER_UNLOCK(intSave);
}

解讀

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

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

sp[REG_PC] = sigcb->context.PC;//指令寄存器
sp[REG_SP] = sigcb->context.USP;//用戶棧堆棧指針, USP指的是 用戶態(tài)的堆棧,即將回到用戶棧繼續(xù)運(yùn)行
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ù)運(yùn)行.

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

@ Description: Software interrupt exception handler
_osExceptSwiHdl: @軟中斷異常處理,注意此時(shí)已在內(nèi)核棧運(yùn)行
@保存任務(wù)上下文(TaskContext) 開始... 一定要對(duì)照TaskContext來理解
SUB     SP, SP, #(4 * 16)	@先申請(qǐng)16個(gè)??臻g單元用于處理本次軟中斷
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)
@ 當(dāng)為用戶模式時(shí),獲取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存儲(chǔ)位置
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)中斷
@恢復(fù)任務(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 恢復(fù)該模式下的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}									 @恢復(fù)R0-R12寄存器
LDMFD   SP, {R13, R14}^                                  @ Restore user mode R13/R14 恢復(fù)用戶模式的R13/R14寄存器
ADD     SP, SP, #(2 * 4)								 @定位到保存舊PC值的位置
LDMFD   SP!, {PC}^                                       @ Return to user 切回用戶模式運(yùn)行
@恢復(fù)任務(wù)上下文(TaskContext) 結(jié)束

編輯:hfy

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

    關(guān)注

    31

    文章

    5253

    瀏覽量

    119205
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10702

    瀏覽量

    209373
  • 信號(hào)處理
    +關(guān)注

    關(guān)注

    48

    文章

    969

    瀏覽量

    102989
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    解析CPU中的寄存器

    8位寄存器在16位寄存器中,而16位寄存器在32位寄存器中。
    發(fā)表于 09-19 10:10 ?3641次閱讀

    淺析從寄存器到用戶態(tài)與內(nèi)核態(tài)

    寄存器CPU內(nèi)部重要的組成部分,寄存器內(nèi)部由N個(gè)觸發(fā)組成,每個(gè)觸發(fā)可以保存1位二進(jìn)制數(shù),所以16位
    的頭像 發(fā)表于 01-30 15:28 ?2527次閱讀

    CPU內(nèi)核寄存器的處理模式是什么

    嵌入式之Cortex-M架構(gòu)CPU內(nèi)核寄存器及處理模式學(xué)習(xí)筆記
    發(fā)表于 12-15 06:13

    寄存器變量

    C語言中使用關(guān)鍵字register來聲明局部變量為寄存器變量。寄存器變量的會(huì)被存放在CPU寄存器中,每當(dāng)需要使用它們時(shí),
    發(fā)表于 06-03 10:13 ?2257次閱讀

    鴻蒙內(nèi)核源碼分析寄存器的本質(zhì)

    寄存器的本質(zhì) 寄存器從大一的計(jì)算機(jī)組成原理就開始聽到它,感覺很神秘,如夢(mèng)如霧多年.揭開本質(zhì)后才發(fā)現(xiàn),寄存器就是一個(gè)32位的存儲(chǔ)空間,一個(gè)int變量而已,但它的厲害之處在于極高頻率的使用,讓人不敢相信
    的頭像 發(fā)表于 04-26 14:51 ?2384次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>分析<b class='flag-5'>寄存器</b>的本質(zhì)

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

    上面的代碼和鴻蒙內(nèi)核方式一樣,都采用了遞減滿的方式, 什么是遞減滿?
    的頭像 發(fā)表于 04-24 11:21 ?1332次閱讀
    <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>

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

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

    深度剖析ARM內(nèi)核寄存器及基本匯編語言1

    M3/M4內(nèi)核寄存器 * 1.2 A7內(nèi)核寄存器 * 1.3 ARM中的PC指針的 * 二、ARM匯編語言
    的頭像 發(fā)表于 04-24 09:59 ?845次閱讀
    深度剖析ARM<b class='flag-5'>內(nèi)核</b><b class='flag-5'>寄存器</b>及基本匯編語言1

    深度剖析ARM內(nèi)核寄存器及基本匯編語言3

    M3/M4內(nèi)核寄存器 * 1.2 A7內(nèi)核寄存器 * 1.3 ARM中的PC指針的 * 二、ARM匯編語言
    的頭像 發(fā)表于 04-24 10:01 ?1059次閱讀
    深度剖析ARM<b class='flag-5'>內(nèi)核</b><b class='flag-5'>寄存器</b>及基本匯編語言3

    基于DUT內(nèi)部寄存器的鏡像

    寄存器模型保持著DUT內(nèi)部寄存器的 鏡像(mirror) 。 鏡像不能保證是正確的,因?yàn)?b class='flag-5'>寄存器模型只能感知到對(duì)這些
    的頭像 發(fā)表于 06-24 12:02 ?761次閱讀

    linux中的進(jìn)程,線程,內(nèi)核的區(qū)別

    大多數(shù)的處理架構(gòu),都有實(shí)現(xiàn)硬件。有專門的指針寄存器,以及特定的硬件指令來完成 入/出
    發(fā)表于 08-18 10:57 ?388次閱讀
    linux中的進(jìn)程<b class='flag-5'>棧</b>,線程<b class='flag-5'>棧</b>,<b class='flag-5'>內(nèi)核</b><b class='flag-5'>棧</b>的區(qū)別

    如何理解 RAMECC FAR 寄存器

    如何理解 RAMECC FAR 寄存器
    的頭像 發(fā)表于 10-19 18:19 ?551次閱讀
    如何理解 RAMECC FAR <b class='flag-5'>寄存器</b>的<b class='flag-5'>值</b>

    CPU的6個(gè)主要寄存器

    CPU寄存器是中央處理內(nèi)的組成部分,是有限存貯容量的高速存貯部件。寄存器CPU內(nèi)部的元件,包括通用
    的頭像 發(fā)表于 02-03 15:15 ?2941次閱讀

    干貨滿滿:ARM的內(nèi)核寄存器講解

    內(nèi)核寄存器與外設(shè)寄存器內(nèi)核寄存器與外設(shè)寄存器是完全不同的概念。
    發(fā)表于 04-17 11:47 ?1905次閱讀
    干貨滿滿:ARM的<b class='flag-5'>內(nèi)核</b><b class='flag-5'>寄存器</b>講解

    寄存器分為基本寄存器和什么兩種

    寄存器是計(jì)算機(jī)中用于存儲(chǔ)數(shù)據(jù)的高速存儲(chǔ)單元,它們是CPU內(nèi)部的重要組成部分。寄存器可以分為基本寄存器和擴(kuò)展寄存器兩種類型。 一、基本
    的頭像 發(fā)表于 07-12 10:31 ?635次閱讀