【README】
1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
2.键盘 是输出型外设;
【1】外设工作原理
【图解】外设工作原理:
- 步骤1:Cpu向外设控制器发出指令(如写命令),向外设控制器中的寄存器(或存储器)读写数据;如显卡控制器;
- 步骤2:写入完成后,外设控制器发出中断请求通知cpu写入完成;
- 步骤3:cpu处理外设中断;
补充:外设驱动主要做3件事情(外设就包括了显示器,键盘,鼠标等)
- 事情1: cpu向外设控制器的寄存器或端口发出读写指令(out指令),最核心的部分;
- 前提是cpu需要知道硬件端口,数据格式等细节,而这些细节非常麻烦;
- 事情2: 操作系统为了隐藏细节,做了一个统一的文件视图,把外设抽象为文件,文件inode结构体就封装了外设端口,数据格式等细节;
- 事情3:cpu处理外设中断(外设控制器在完成相应操作后会向cpu发出中断) ;
补充2: 其他外设工作原理类似,同上述外设驱动的3件事情;
【2】键盘驱动代码实现
1)键盘中断初始化
【图解】
1)0x21号中断是键盘中断,键盘中断处理程序为 keyboard_interrupt ;
2) _keyboard_interrupe 代码
_keyboard_interrupt:
// 把0x60端口里的数据读入到al寄存器;
inb $0x60, %al
// 调用 key_table + eax*4
call key_table(, %eax, 4)
3)处理扫描码 key_table + eax*4
【图解】
调用 do_self 函数找到键盘按键对应的ASCII码;
3)从 key_map 取出 ASCII 码
【图解】
- 说明1)key_map: .byte 0, 27 .ascii “1234567890-=” ...
- 说明2)shift_map: .byte 0, 27 .ascii “!@#$%^&*()_+” ... // 按住shift键
- 说明3)Movb(%ebx, %eax), %al // 把找到键盘按键的ASCII 送入al寄存器
- 说明4) call put_queue 把ASCII 送入缓冲区(或队列)
4)put_queue 把 ASCII 放入缓冲区 con.read_q
【图解】
con.read_ q 中 con 指的是 console 控制器;
Read_q 指的是 读缓冲区;
补充:回显(可显示字符才会回显)
// 字符回显(打出在显示器)
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);
...
}
【3】键盘处理总结
【3.1】键盘工作步骤
【图解】键盘工作步骤
- 步骤1:操作系统启动时初始化键盘中断;
- 步骤2:敲下键盘,发出键盘中断,cpu响应中断,执行键盘中断处理程序 keyboard_interrupt ;
- 步骤3:keyboard_interrupt 程序从键盘控制器端口读取数据到al寄存器;
- 步骤4:调用 do_self 函数找到键盘按键对应的ASCII码
- 步骤5:把 ASCII 放入缓冲区 con.read_q
- 步骤6:判断字符是否需要回显,若需要,则回显到屏幕上;
【3.2】操作系统输入输出外设工作原理
把键盘处理流程与显示器处理流程合并在一起,如下图。
因为键盘是输出型外设,显示器是输入型外设;
【图解】
1)外设对外暴露的都是文件接口;
2)Tty设备读函数是 tty_read,tty设备写函数是 tty_write ; 分别操作 read_q 队列,write_q队列;(队列可以理解为缓冲区)