ucore操作系統(tǒng)實(shí)驗(yàn)筆記 - 重新理解中斷

在上一篇文章ucore操作系統(tǒng)實(shí)驗(yàn)筆記 - Lab1中,我已經(jīng)比較詳細(xì)地記錄了中斷的使用叮阅。那篇文章關(guān)于中斷的重點(diǎn)是如何使用IDT躺苦、中斷描述符和中斷向量表等。這篇文章我將把重點(diǎn)放到另外一個(gè)地方能庆,也就是中斷的過程中如何保存和恢復(fù)現(xiàn)場(chǎng)九杂。

CPU接收到中斷信號(hào)后會(huì)做什么

  1. CPU在執(zhí)行完當(dāng)前程序的每一條指令后颁湖,都會(huì)去確認(rèn)在執(zhí)行剛才的指令過程中中斷控制器(如:8259A)是否發(fā)送中斷請(qǐng)求過來,如果有那么CPU就會(huì)在相應(yīng)的時(shí)鐘脈沖到來時(shí)從總線上讀取中斷請(qǐng)求對(duì)應(yīng)的中斷向量例隆;
  1. CPU根據(jù)得到的中斷向量(以此為索引)到IDT中找到該向量對(duì)應(yīng)的中斷描述符甥捺,中斷描述符里保存著中斷服務(wù)例程的段選擇子;
  2. CPU使用IDT查到的中斷服務(wù)例程的段選擇子從GDT中取得相應(yīng)的段描述符镀层,段描述符里保存了中斷服務(wù)例程的段基址和屬性信息镰禾,此時(shí)CPU就得到了中斷服務(wù)例程的起始地址,并跳轉(zhuǎn)到該地址唱逢;
  3. CPU會(huì)根據(jù)CPL和中斷服務(wù)例程的段描述符的DPL信息確認(rèn)是否發(fā)生了特權(quán)級(jí)的轉(zhuǎn)換吴侦。比如當(dāng)前程序正運(yùn)行在用戶態(tài),而中斷程序是運(yùn)行在內(nèi)核態(tài)的惶我,則意味著發(fā)生了特權(quán)級(jí)的轉(zhuǎn)換妈倔,這時(shí)CPU會(huì)從當(dāng)前程序的TSS信息(該信息在內(nèi)存中的起始地址存在TR寄存器中)里取得該程序的內(nèi)核棧地址博投,即包括內(nèi)核態(tài)的ss和esp的值绸贡,并立即將系統(tǒng)當(dāng)前使用的棧切換成新的內(nèi)核棧。這個(gè)棧就是即將運(yùn)行的中斷服務(wù)程序要使用的棧毅哗。緊接著就將當(dāng)前程序使用的用戶態(tài)的ss和esp壓到新的內(nèi)核棧中保存起來听怕;
  4. CPU需要開始保存當(dāng)前被打斷的程序的現(xiàn)場(chǎng)(即一些寄存器的值),以便于將來恢復(fù)被打斷的程序繼續(xù)執(zhí)行虑绵。這需要利用內(nèi)核棧來保存相關(guān)現(xiàn)場(chǎng)信息尿瞭,即依次壓入當(dāng)前被打斷程序使用的eflags,cs翅睛,eip声搁,errorCode(如果是有錯(cuò)誤碼的異常)信息;
  5. CPU利用中斷服務(wù)例程的段描述符將其第一條指令的地址加載到cs和eip寄存器中捕发,開始執(zhí)行中斷服務(wù)例程疏旨。這意味著先前的程序被暫停執(zhí)行,中斷服務(wù)程序正式開始工作扎酷。

上面這些內(nèi)容是我從ucore實(shí)驗(yàn)指導(dǎo)書上直接摘抄下來的檐涝,在之前那篇文章中,我主要關(guān)注前3步和最后一步,這篇文章谁榜,我將關(guān)注第4幅聘、5步。

特權(quán)級(jí)轉(zhuǎn)換的檢測(cè)

我個(gè)人覺得第4窃植、5步應(yīng)該是發(fā)生在CPU跳轉(zhuǎn)到ISR(中斷服務(wù)例程)之前帝蒿,所以把第3步放在第5步的后面更合適,之后我會(huì)解釋為什么我這么覺得巷怜。當(dāng)CPU獲取到IDT中的中斷描述符后陵叽,會(huì)對(duì)特權(quán)級(jí)的轉(zhuǎn)換進(jìn)行一次檢測(cè),具體檢測(cè)如下圖所示:

特權(quán)級(jí)轉(zhuǎn)換的檢測(cè)

當(dāng)CPU獲取了中斷描述符后丛版,CPU會(huì)用中斷描述符的DPL和當(dāng)前段選擇子的CPL進(jìn)行比較巩掺,從而判斷是否需要進(jìn)行特權(quán)級(jí)的轉(zhuǎn)換。同時(shí)页畦,它還會(huì)做一些列的檢測(cè)工作胖替,比如對(duì)于硬中斷而言,CPL一定要大于等于DPL豫缨,因?yàn)樘貦?quán)級(jí)是向著更高特權(quán)級(jí)或者平級(jí)轉(zhuǎn)換的独令。而對(duì)于軟中斷而言,轉(zhuǎn)換后的特權(quán)級(jí)不能超過轉(zhuǎn)換前的特權(quán)級(jí)好芭,這是為了防止用戶代碼隨意觸發(fā)中斷燃箭。對(duì)于CPL和DPL不同的情況,我們需要使用TSS來對(duì)內(nèi)核棧進(jìn)行切換舍败,關(guān)于TSS的內(nèi)容我之后會(huì)單獨(dú)開篇文章招狸。

內(nèi)核棧的變化

第4、5步一個(gè)重要的功能就是向內(nèi)核棧中壓入各種寄存器邻薯。壓入這些寄存器既可以起到保存現(xiàn)場(chǎng)的作用裙戏,又能讓ISR知道中斷的各種信息,所以這兩步是很重要的厕诡。我們來看看哪些寄存器是CPU必須壓入內(nèi)核棧的:

內(nèi)核棧的變化

這是發(fā)生中斷并且特權(quán)級(jí)轉(zhuǎn)換后椑郯瘢空間變化的示意圖,對(duì)于不發(fā)生特權(quán)級(jí)轉(zhuǎn)換的中斷灵嫌,有兩個(gè)地方不同壹罚,第一,它只用到一個(gè)棧寿羞,也就是說Procedure和Handler用的是同一個(gè)棧猖凛;第二,CPU不需要壓入SS和ESP稠曼。除此之外形病,這兩種情況都需要壓入CS客年,EIP和Error Code(如果有的話)。之所以我說第3步應(yīng)該在第5步后漠吻,原因就在這里量瓜,如果先跳到了ISR,那么壓入的EIP就是ISR中的EIP了途乃,并不是中斷前的EIP绍傲,因此我們應(yīng)該在第3步前完成步驟4和5。

Trapframe和ISR

除了CPU要壓入的各種寄存器耍共,我們還需要壓入其他一些寄存器用于保存現(xiàn)場(chǎng)和提供給ISR中斷信息烫饼。在ucore中,我們使用結(jié)構(gòu)體trapframe來將保存的寄存器傳給ISR试读。下面就先來看看trapframe:

/* registers as pushed by pushal */
struct pushregs {
    uint32_t reg_edi;
    uint32_t reg_esi;
    uint32_t reg_ebp;
    uint32_t reg_oesp;          /* Useless */
    uint32_t reg_ebx;
    uint32_t reg_edx;
    uint32_t reg_ecx;
    uint32_t reg_eax;
};

struct trapframe {
    struct pushregs tf_regs;
    uint16_t tf_gs;
    uint16_t tf_padding0;
    uint16_t tf_fs;
    uint16_t tf_padding1;
    uint16_t tf_es;
    uint16_t tf_padding2;
    uint16_t tf_ds;
    uint16_t tf_padding3;
    uint32_t tf_trapno;
    /* below here defined by x86 hardware */
    uint32_t tf_err;
    uintptr_t tf_eip;
    uint16_t tf_cs;
    uint16_t tf_padding4;
    uint32_t tf_eflags;
    /* below here only when crossing rings, such as from user to kernel */
    uintptr_t tf_esp;
    uint16_t tf_ss;
    uint16_t tf_padding5;
} __attribute__((packed));

其中pushregs中的寄存器都是pushal中需要壓入棧的所有寄存器杠纵。有了這個(gè)數(shù)據(jù)結(jié)構(gòu)后,我們就可以在中斷后獲取中斷的信息钩骇,并將它傳給ISR比藻,ISR會(huì)根據(jù)傳入的trapframe來進(jìn)行相應(yīng)的操作。
下面我們來看看如何給trapframe賦值倘屹,如何將trapframe傳給ISR:

.globl vector2
vector2:
  pushl $0
  pushl $2
  jmp __alltraps

上面這段代碼是中斷向量2银亲,在第6步時(shí)CPU會(huì)執(zhí)行這里的指令。它首先壓入0和2纽匙,0是error code(對(duì)于沒有error code的中斷务蝠,ISR會(huì)壓入0作為error code;如果中斷有error code烛缔,這里就不會(huì)壓入0)馏段,2是中斷向量號(hào)。注意力穗,在這之前毅弧,CPU已經(jīng)壓入了EFLAGS,CS当窗,EIP和Error Code(如果有的話)。在壓入error code和中斷向量號(hào)后寸宵,CPU跳到__alltraps崖面,__alltraps會(huì)將所有中斷需要保存的寄存器存到內(nèi)核棧,然后將此時(shí)棧頂?shù)牡刂?$esp)作為參數(shù)傳給trap()梯影,trap()會(huì)將此時(shí)棧中壓入的各種寄存器整體當(dāng)成trapframe來處理巫员。trap()會(huì)會(huì)根據(jù)trapframe中的內(nèi)容,對(duì)中斷進(jìn)行相應(yīng)的處理甲棍。

.text
.globl __alltraps
__alltraps:
    # push registers to build a trap frame
    # therefore make the stack look like a struct trapframe
    pushl %ds
    pushl %es
    pushl %fs
    pushl %gs
    pushal

這段代碼將所有中斷需要保存的寄存器壓入內(nèi)核棧简识。

 # load GD_KDATA into %ds and %es to set up data segments for kernel
    movl $GD_KDATA, %eax
    movw %ax, %ds
    movw %ax, %es

這段代碼將此時(shí)的數(shù)據(jù)段和附加段設(shè)置為內(nèi)核的數(shù)據(jù)段(ISR是位于kernel的)。

 # push %esp to pass a pointer to the trapframe as an argument to trap()
    pushl %esp

    # call trap(tf), where tf=%esp
    call trap

這段代碼先將%esp的值壓入內(nèi)核棧,%esp的值將作為函數(shù)trap()的參數(shù)七扰,然后我們?cè)賑all trap奢赂。通過向棧中壓入各種寄存器的信息并且將棧頂?shù)牡刂纷鳛閠rapframe的地址,我們完成了對(duì)trapframe的賦值颈走。trap()函數(shù)接收到trapframe后就可以根據(jù)中斷類型做出相應(yīng)處理了膳灶。我們來看看此時(shí)棧中的情況:

內(nèi)核棧

因?yàn)闂J菑母叩刂废虻偷刂飞L(zhǎng)的,因此立由,棧中藍(lán)色部分EFLAGS地址最高轧钓,EDI地址最低。這個(gè)和trapframe中的元素也是吻合的锐膜,tf_eflags地址最高(如何不考慮tf_esp, tf_ss)毕箍,而reg_edi地址最低。因此我們可以通過Old ESP這個(gè)地址道盏,把棧中藍(lán)色部分當(dāng)成trapframe來處理霉晕。

# pop the pushed stack pointer
    popl %esp

    # return falls through to trapret...
.globl __trapret
__trapret:
    # restore registers from stack
    popal

    # restore %ds, %es, %fs and %gs
    popl %gs
    popl %fs
    popl %es
    popl %ds

    # get rid of the trap number and error code
    addl $0x8, %esp
    iret

當(dāng)trap()運(yùn)行結(jié)束后,我們需要將寄存器恢復(fù)到中斷前的狀態(tài)捞奕。在這里牺堰,我們只需要將內(nèi)核棧中的內(nèi)容分別彈出,并保存到相應(yīng)的寄存器即可颅围。最后伟葫,通過調(diào)用iret指令來恢復(fù)EIP,CS和EFLAGS院促。如果還存在特權(quán)級(jí)的轉(zhuǎn)化筏养,我們還需要彈出之前保存的SS和ESP。到此為止常拓,整個(gè)中斷的過程就結(jié)束了渐溶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弄抬,隨后出現(xiàn)的幾起案子茎辐,更是在濱河造成了極大的恐慌,老刑警劉巖掂恕,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拖陆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡懊亡,警方通過查閱死者的電腦和手機(jī)依啰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來店枣,“玉大人速警,你說我怎么就攤上這事叹誉。” “怎么了闷旧?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵长豁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鸠匀,道長(zhǎng)蕉斜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任缀棍,我火速辦了婚禮宅此,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爬范。我一直安慰自己父腕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布青瀑。 她就那樣靜靜地躺著璧亮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斥难。 梳的紋絲不亂的頭發(fā)上枝嘶,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音哑诊,去河邊找鬼群扶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛镀裤,可吹牛的內(nèi)容都是我干的竞阐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼暑劝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼骆莹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起担猛,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤幕垦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后毁习,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體智嚷,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年纺且,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稍浆。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡载碌,死狀恐怖猜嘱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嫁艇,我是刑警寧澤朗伶,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站步咪,受9級(jí)特大地震影響论皆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猾漫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一点晴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悯周,春花似錦粒督、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闰挡,卻和暖如春锐墙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背长酗。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工溪北, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人花枫。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓刻盐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親劳翰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敦锌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容