你的鍵盤(pán)是什么時(shí)候生效的?
當(dāng)你的計(jì)算機(jī)剛剛啟動(dòng)時(shí),你按下鍵盤(pán)是不生效的,但是過(guò)了一段時(shí)間后,再按下鍵盤(pán)就有效果了。

那我們今天就來(lái)刨根問(wèn)底一下,到底過(guò)了多久之后,按下鍵盤(pán)才有效果呢?
當(dāng)然首先你得知道,按下鍵盤(pán)后會(huì)觸發(fā)中斷,CPU 收到你的鍵盤(pán)中斷后,根據(jù)中斷號(hào),尋找由操作系統(tǒng)寫(xiě)好的鍵盤(pán)中斷處理程序。
中斷的原理和過(guò)程不了解的,可以看我的文章,認(rèn)認(rèn)真真的聊聊中斷
這個(gè)中斷處理程序會(huì)把你的鍵盤(pán)碼放入一個(gè)隊(duì)列中,由相應(yīng)的用戶(hù)程序或內(nèi)核程序讀取,并顯示在控制臺(tái),或者其他用途,這就代表你的鍵盤(pán)生效了。
不過(guò)放寬心,我們不展開(kāi)講這個(gè)中斷處理程序以及用戶(hù)程序讀取鍵盤(pán)碼后的處理細(xì)節(jié),我們把關(guān)注點(diǎn)放在,究竟是“什么時(shí)候”,按下鍵盤(pán)才會(huì)有這個(gè)效果。
我們以 Linux 0.11 源碼為例,發(fā)現(xiàn)進(jìn)入內(nèi)核的 main 函數(shù)后不久,有這樣一行代碼。
void?main(void)?{
????...
????trap_init();
????...
}
看到這個(gè)方法的全部代碼后,你可能會(huì)會(huì)心一笑,也可能一臉懵逼。
void?trap_init(void)?{
????int?i;
????set_trap_gate(0,÷_error);
????set_trap_gate(1,&debug);
????set_trap_gate(2,&nmi);
????set_system_gate(3,&int3);???/*?int3-5?can?be?called?from?all?*/
????set_system_gate(4,&overflow);
????set_system_gate(5,&bounds);
????set_trap_gate(6,&invalid_op);
????set_trap_gate(7,&device_not_available);
????set_trap_gate(8,&double_fault);
????set_trap_gate(9,&coprocessor_segment_overrun);
????set_trap_gate(10,&invalid_TSS);
????set_trap_gate(11,&segment_not_present);
????set_trap_gate(12,&stack_segment);
????set_trap_gate(13,&general_protection);
????set_trap_gate(14,&page_fault);
????set_trap_gate(15,&reserved);
????set_trap_gate(16,&coprocessor_error);
????for?(i=17;i<48;i++)
????????set_trap_gate(i,&reserved);
????set_trap_gate(45,&irq13);
????set_trap_gate(39,¶llel_interrupt);
}
這啥玩意?這么多 set_xxx_gate。
void?trap_init(void)?{
????int?i;
????//?set?了一堆?trap_gate
????set_trap_gate(0,?÷_error);
????...?
????//?又?set?了一堆?system_gate
????set_system_gate(45,?&bounds);
????...
????//?又又批量?set?了一堆?trap_gate
????for?(i=17;i<48;i++)
????????set_trap_gate(i,?&reserved);
????...
}
#define?_set_gate(gate_addr,type,dpl,addr)?\
__asm__?("movw?%%dx,%%ax\n\t"?\
????"movw?%0,%%dx\n\t"?\
????"movl?%%eax,%1\n\t"?\
????"movl?%%edx,%2"?\
????:?\
????:?"i"?((short)?(0x8000+(dpl<<13)+(type<<8))),?\
????"o"?(*((char?*)?(gate_addr))),?\
????"o"?(*(4+(char?*)?(gate_addr))),?\
????"d"?((char?*)?(addr)),"a"?(0x00080000))
#define?set_trap_gate(n,addr)?\
????_set_gate(&idt[n],15,0,addr)
#define?set_system_gate(n,addr)?\
????_set_gate(&idt[n],15,3,addr)

set_trap_gate(0,÷_error);
set_system_gate(5,&overflow);
TIPS:這個(gè) system 與 trap 的區(qū)別僅僅在于,設(shè)置的中斷描述符的特權(quán)級(jí)不同,前者是 0(內(nèi)核態(tài)),后者是 3(用戶(hù)態(tài)),這塊展開(kāi)將會(huì)是非常嚴(yán)謹(jǐn)?shù)摹⒗@口的、復(fù)雜的特權(quán)級(jí)相關(guān)的知識(shí),不明白的話先不用管,就理解為都是設(shè)置一個(gè)中斷號(hào)和中斷處理程序的對(duì)應(yīng)關(guān)系就好了。
void?trap_init(void)?{
????...
????for?(i=17;i<48;i++)
????????set_trap_gate(i,&reserved);
????...
}

void?main(void)?{
????...
????trap_init();
????...
????tty_init();
????...
}
void?tty_init(void)?{
????rs_init();
????con_init();
}
void?con_init(void)?{
????...
????set_trap_gate(0x21,&keyboard_interrupt);
????...
}
void?main(void)?{
????...
????trap_init();
????...
????tty_init();
????...
????sti();
????...
}

本文可以當(dāng)做?你管這破玩意叫操作系統(tǒng)源碼 系列文章的第 14 回。
為了讓不追更系列的讀者也能很方便閱讀并學(xué)到東西,我把它改造成了單獨(dú)的不依賴(lài)系列上下文的文章,具體原因可以看?堅(jiān)持不下去了...
點(diǎn)擊下方的閱讀原文可以跳轉(zhuǎn)到本系列的 GitHub 頁(yè),那里也有完整目錄和規(guī)劃,以及一些輔助的資料,歡迎提出各種問(wèn)題。
