鍵盤中斷初始化和處理
提取的代碼如下:
// con_init 函數,初始化控制臺(包括鍵盤)的中斷
void con_init(void) {set_trap_gate(0x21, &keyboard_interrupt);
}
?
// 鍵盤中斷處理函數
.globl _keyboard_interrupt
_keyboard_interrupt:inb $0x60, %al // 從端口0x60讀掃描碼call key_table(%eax, 4) // 調用key_table+eax*4push $0call _do_tty_interrupt
總結:
這段代碼描述了鍵盤中斷初始化和處理的過程:
-
鍵盤中斷初始化 (
con_init
函數):-
con_init
函數設置鍵盤中斷門(trap gate),將鍵盤中斷處理函數keyboard_interrupt
地址加載到中斷向量0x21
。 -
這個函數是控制臺(包括鍵盤)初始化的一部分,確保鍵盤輸入能夠觸發中斷。
-
-
鍵盤中斷處理 (
_keyboard_interrupt
函數):-
當鍵盤被敲擊時,產生中斷信號。
-
中斷處理函數
_keyboard_interrupt
被調用,從端口0x60
讀取鍵盤掃描碼。 -
調用
key_table
函數處理掃描碼,key_table
是一個查找表,用于將掃描碼轉換為字符。 -
將
0
壓棧,然后調用_do_tty_interrupt
函數進一步處理字符輸出。
-
當用戶敲擊鍵盤時,觸發中斷,操作系統捕獲中斷并調用相應的中斷處理函數來讀取和處理鍵盤輸入。這是實現鍵盤輸入功能的關鍵步驟。
處理鍵盤輸入的掃描碼
提取的代碼如下:
// 在 kernel/chr_drv/keyboard.s 中定義的 key_table
key_table:.long none, do_self, do_self, do_self // 掃描碼00-03.long do_self, ..., func, scroll, cursor 等等
?
// do_self 函數
do_self:lea alt_map, %ebxtestb $0x20, mode // alt鍵是否同時按下jne 1flea shift_map, %ebxtestb $0x03, modejne 1flea key_map, %ebx
1:
總結:
這段代碼展示了 Linux 內核如何處理鍵盤輸入的掃描碼,并將它們轉換為字符輸出。具體來說:
-
定義
key_table
函數數組:-
key_table
是一個函數數組,用于處理不同的鍵盤掃描碼。每個條目對應一個掃描碼的處理函數。
-
-
處理掃描碼:
-
掃描碼
02
對應按鍵1
,01
對應ESC
,12
對應E
等。
-
-
處理函數
do_self
:-
do_self
函數用于處理特定的掃描碼。 -
它首先加載
alt_map
到ebx
寄存器。 -
檢查
mode
寄存器,確定alt
鍵是否被按下。 -
如果
alt
鍵被按下,它加載shift_map
到ebx
寄存器。 -
否則,它加載
key_map
到ebx
寄存器。 -
key_map
、shift_map
和alt_map
是映射表,用于將掃描碼轉換為相應的字符。
-
-
映射表:
-
映射表(如
key_map
)用于將掃描碼轉換為字符。例如,a
的key_map
映射為a
,而shift_map
映射為A
。
-
這個過程展示了如何通過映射表和條件邏輯將鍵盤掃描碼轉換為字符,并將它們輸出到屏幕上。這是鍵盤輸入處理的核心部分,它確保了用戶輸入能夠被正確識別和顯示。
從 key_map
中取出對應的 ASCII 碼
提取的代碼如下:
#if defined(KBD_US)
key_map: .byte 0,27 .ascii "1234567890-="
shift_map: .byte 0,27 .ascii "!@#$%^&*()_+"
#elif defined(KBD_GR)...
#endif
?
# 繼續do_self,從1f開始,ebx放的是map起始地址
1: movb (%ebx, %eax), %al // 掃描碼索引,ASCII碼->alorb %al, %al ? ? je none // 沒有對應的ASCII碼testb $0x4c, mode // 看caps是否亮je 2f ?cmpb $'a, %al jb 2fcmpb $'}, %al ? ja 2f subb $32, %al // 變大寫
2: testb $??, mode // 處理其他模式,如ctrl同時按下
3: andl $0xff, %eax call put_queue
none: ret
總結:
這段代碼展示了如何從 key_map
中取出對應的 ASCII 碼,并根據鍵盤的模式(如大寫鎖定 Caps Lock
或 Shift
鍵)進行相應的處理。以下是詳細步驟:
-
定義鍵盤映射表:
-
key_map
和shift_map
是兩個映射表,分別用于處理未按下Shift
鍵和按下Shift
鍵時的鍵盤輸入。 -
這些映射表將鍵盤掃描碼轉換為 ASCII 碼。
-
-
從
key_map
中取出 ASCII 碼:-
根據掃描碼索引從
key_map
中取出對應的 ASCII 碼。 -
如果沒有找到對應的 ASCII 碼,則跳轉到
none
標簽。
-
-
處理大寫鎖定:
-
檢查
Caps Lock
模式是否激活(testb $0x4c, mode
)。 -
如果激活,將小寫字母轉換為大寫字母(
cmpb $'a, %al
和subb $32, %al
)。
-
-
處理其他模式:
-
檢查是否有其他模式激活,如
Ctrl
鍵同時按下(testb $??, mode
)。 -
根據需要處理這些模式。
-
-
將字符放入隊列:
-
將處理后的 ASCII 碼放入輸出隊列(
put_queue
)。
-
-
返回:
-
如果沒有找到對應的 ASCII 碼,則返回(
none: ret
)。
-
處理鍵盤輸入并將字符放入隊列中
提取的代碼如下:
// put_queue 函數
put_queue:movl _table_list, %edxmovl head(%edx), %ecx
1:movb %al, buf(%edx, %ecx)...
// do_tty_interrupt 函數
void do_tty_interrupt(int tty) {copy_to_cooked(tty_table + tty);
}
?
// copy_to_cooked 函數
void copy_to_cooked(struct tty_struct *tty) {GETCH(tty->read_q, c);if (L_ECHO(tty)) { // 回顯,也可以不回顯PUTCH(c, tty->write_q);tty->write(tty); } // 立刻顯示到屏幕上PUTCH(c, tty->secondary); // 完成copy_to_cooked... wake_up(&tty->secondary.proc_list);
}
總結:
這段代碼描述了 Linux 內核如何處理鍵盤輸入并將字符放入隊列中的過程:
-
put_queue
函數:-
該函數負責將字符放入
con.read_q
隊列中。 -
它首先將
_table_list
地址加載到edx
寄存器,然后獲取head
指針到ecx
寄存器。 -
接著,它從緩沖區
buf
中取出字符并放入隊列中。
-
-
do_tty_interrupt
函數:-
該函數處理鍵盤中斷,調用
copy_to_cooked
函數來處理鍵盤輸入。
-
-
copy_to_cooked
函數:-
該函數從
tty->read_q
隊列中獲取字符。 -
如果
L_ECHO
標志被設置(表示回顯),則將字符放入tty->write_q
隊列中。 -
然后調用
tty->write(tty)
函數將字符立刻顯示到屏幕上。 -
將字符放入
tty->secondary
隊列中,完成copy_to_cooked
操作。 -
最后,喚醒等待在
tty->secondary.proc_list
隊列中的進程。
-
這個過程展示了如何將鍵盤輸入的字符處理并放入隊列中,以便后續顯示在屏幕上。
鍵盤處理的步驟總結
-
中斷初始化:
-
鍵盤作為控制臺(console)的一部分,其中斷通過
con_init
函數進行初始化,設置中斷門(trap gate)以響應鍵盤事件。
-
-
中斷處理:
-
當用戶敲擊鍵盤時,產生中斷信號,操作系統通過中斷處理程序
keyboard_interrupt
來響應這個中斷。 -
中斷處理程序從鍵盤的端口(通常是
0x60
)讀取掃描碼。
-
-
掃描碼轉換:
-
根據讀取到的掃描碼,調用
key_table
函數數組來查找對應的處理函數或映射表。 -
掃描碼對應鍵盤上的不同按鍵,如
02
對應數字鍵1
,01
對應ESC
鍵等。
-
-
字符映射:
-
對于顯示字符,使用
key_map
映射表將掃描碼轉換為 ASCII 碼。 -
如果按下
Shift
或Alt
等修飾鍵,可能會使用shift_map
或alt_map
來獲取大寫字母或特殊字符。
-
-
隊列處理:
-
將轉換得到的 ASCII 碼放入
write_q
隊列中,等待顯示設備(如顯示器)來讀取并顯示。 -
如果設置了回顯(
L_ECHO
),則同時將字符放入read_q
隊列中,以便用戶可以看到自己輸入了什么。
-
-
顯示字符:
-
con_write
函數負責從write_q
隊列中取出字符并通過顯示器驅動程序tty_write
輸出到屏幕上。
-
-
緩沖和回顯:
-
copy_to_cooked
函數處理read_q
隊列中的字符,可能包括回顯邏輯,即將字符回顯到屏幕上。
-
到顯示器和鍵盤的交互
這張圖展示了Linux內核中字符設備(特別是TTY設備)的讀寫流程。以下是流程的總結:
寫流程(從上到下):
-
系統調用 (write) :應用程序通過系統調用
write
向內核請求寫操作。 -
字符設備接口 (crw_table[]) :系統調用被路由到字符設備接口,具體到
char_dev.c
文件中的相關函數。 -
TTY設備寫 (tty_write) :字符設備接口調用
tty_write
函數,該函數位于tty_io.c
文件中。 -
write_q隊列:寫操作的數據被放入
write_q
隊列中。 -
顯示器寫 (con_write) :數據從
write_q
隊列被取出并通過con_write
函數寫入顯示器,該函數位于console.c
文件中。 -
顯示器:最終數據被顯示在顯示器上。
讀流程(從下到上):
-
主機鍵盤:用戶通過鍵盤輸入數據。
-
keyboard.S:鍵盤輸入的數據被處理并放入
read_q
隊列中。 -
read_q隊列:鍵盤輸入的數據存儲在
read_q
隊列中。 -
TTY設備讀 (tty_read) :應用程序通過系統調用
read
請求讀操作,該請求被路由到tty_read
函數,該函數位于tty_io.c
文件中。 -
字符設備接口 (crw_table[]) :
tty_read
函數通過字符設備接口返回數據給應用程序。 -
系統調用 (read) :最終數據被返回給應用程序。
回顯流程:
-
回顯:鍵盤輸入的數據不僅被放入
read_q
隊列,還會被回顯到write_q
隊列中。 -
顯示器寫 (con_write) :回顯的數據通過
con_write
函數寫入顯示器,顯示用戶輸入的內容。
總結:
-
寫操作:從應用程序的
write
系統調用開始,經過字符設備接口、TTY設備寫、寫隊列,最終寫入顯示器。 -
讀操作:從鍵盤輸入開始,經過鍵盤處理、讀隊列、TTY設備讀、字符設備接口,最終返回給應用程序的
read
系統調用。 -
回顯:鍵盤輸入的數據會被同時寫入讀隊列和寫隊列,實現輸入內容的即時顯示。
這個流程展示了Linux內核中字符設備(特別是TTY設備)的讀寫機制,確保了數據的正確傳輸和顯示。