<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你的鍵盤(pán)是什么時(shí)候生效的?

          共 1224字,需瀏覽 3分鐘

           ·

          2021-12-31 12:49

          當(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

          ?
          有密集恐懼癥的話,絕對(duì)看不下去這個(gè)代碼,所以我就給他簡(jiǎn)化一下。
          ?
          把相同功能的去掉。
          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);
          ????...
          }
          這就簡(jiǎn)單多了,我們一塊一塊看。
          ?
          首先我們看 set_trap_gateset_system_gate 這倆貨,發(fā)現(xiàn)了這么幾個(gè)宏定義。
          #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)

          別怕,我也看不懂。
          ?
          不過(guò)這倆都是最終指向了相同的另一個(gè)宏定義 _set_gate,說(shuō)明是有共性的。
          ?
          啥共性呢?我直接說(shuō)吧,那段你完全看不懂的代碼,是將匯編語(yǔ)言嵌入到 c 語(yǔ)言了,這種內(nèi)聯(lián)匯編的格式非常惡心,所以我也不想搞懂它,最終的效果就是在中斷描述符表中插入了一個(gè)中斷描述符
          ?
          中斷描述符表還記得吧,英文叫 idt。
          ?
          ?
          這段代碼就是往這個(gè) idt 表里一項(xiàng)一項(xiàng)地寫(xiě)東西,其對(duì)應(yīng)的中斷號(hào)就是第一個(gè)參數(shù),中斷處理程序就是第二個(gè)參數(shù)。

          產(chǎn)生的效果就是,之后如果來(lái)一個(gè)中斷后,CPU 根據(jù)其中斷號(hào),就可以到這個(gè)中斷描述符表 idt?中找到對(duì)應(yīng)的中斷處理程序了。
          ?
          比如這個(gè)。
          set_trap_gate(0,÷_error);
          就是設(shè)置 0 號(hào)中斷,對(duì)應(yīng)的中斷處理程序是 divide_error

          等 CPU 執(zhí)行了一條除零指令的時(shí)候,會(huì)從硬件層面發(fā)起一個(gè) 0 號(hào)異常中斷,然后執(zhí)行由我們操作系統(tǒng)定義的 divide_error 也就是除法異常處理程序,執(zhí)行完之后再返回。
          ?
          再比如這個(gè)。
          set_system_gate(5,&overflow);
          就是設(shè)置 5 號(hào)中斷,對(duì)應(yīng)的中斷處理程序是 overflow,是邊界出錯(cuò)中斷。
          ?

          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);
          ????...
          }
          17 到 48 號(hào)中斷都批量設(shè)置為了 reserved 函數(shù),這是暫時(shí)的,后面各個(gè)硬件初始化時(shí)要重新設(shè)置好這些中斷,把暫時(shí)的這個(gè)給覆蓋掉,此時(shí)你留個(gè)印象。
          ?
          所以整段代碼執(zhí)行下來(lái),內(nèi)存中那個(gè) idt 的位置會(huì)變成如下的樣子。
          ?
          ?
          好了,我們看到了設(shè)置中斷號(hào)與中斷處理程序?qū)?yīng)的地方,那這行代碼過(guò)去后,鍵盤(pán)好使了么?

          NO

          鍵盤(pán)產(chǎn)生的中斷的中斷號(hào)是 0x21,此時(shí)這個(gè)中斷號(hào)還僅僅對(duì)應(yīng)著一個(gè)臨時(shí)的中斷處理程序?&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);
          ????...
          }
          我省略了大量的代碼,只保留了我們關(guān)心的。

          注意到 trap_init 后有個(gè) tty_init,最后根據(jù)調(diào)用鏈,會(huì)調(diào)用到一行添加 0x21 號(hào)中斷處理程序的代碼,就是剛剛熟悉的 set_trap_gate

          而后面的 keyboard_interrupt?根據(jù)名字也可以猜出,就是鍵盤(pán)的中斷處理程序嘛!

          好了,那我們終于找到大案了,就是從這一行代碼開(kāi)始,我們的鍵盤(pán)生效了!

          沒(méi)錯(cuò),不過(guò)還有點(diǎn)小問(wèn)題,不過(guò)不重要,就是我們現(xiàn)在的中斷處于禁用狀態(tài),不論是鍵盤(pán)中斷還是其他中斷,通通都不好使。

          而 main 方法繼續(xù)往下讀,還有一行這個(gè)東西。
          void?main(void)?{
          ????...
          ????trap_init();
          ????...
          ????tty_init();
          ????...
          ????sti();
          ????...
          }
          sti 最終會(huì)對(duì)應(yīng)一個(gè)同名的匯編指令 sti,表示允許中斷。所以這行代碼之后,鍵盤(pán)才真正開(kāi)始生效!



          動(dòng)畫(huà)酷不酷?好啦,今天的文章就到這里了,中斷的原理和細(xì)節(jié),就看我之前的文章,認(rèn)認(rèn)真真的聊聊中斷

          鍵盤(pán)處理的具體流程,可以跟著我今天的代碼深入進(jìn)去看看喲,Linux 0.11 里還是很簡(jiǎn)單的。


          ------


          本文可以當(dāng)做?你管這破玩意叫操作系統(tǒng)源碼 系列文章的第 14 回。


          為了讓不追更系列的讀者也能很方便閱讀并學(xué)到東西,我把它改造成了單獨(dú)的不依賴(lài)系列上下文的文章,具體原因可以看?堅(jiān)持不下去了...


          點(diǎn)擊下方的閱讀原文可以跳轉(zhuǎn)到本系列的 GitHub 頁(yè),那里也有完整目錄和規(guī)劃,以及一些輔助的資料,歡迎提出各種問(wèn)題。

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久99久视频精品 | 热久久国产精品视频 | 午夜成人毛片 | 91精品久| 久操新在线 |