共计 1890 个字符,预计需要花费 5 分钟才能阅读完成。
系列目录
- 序篇
- 筹备工作
- BIOS 启动到实模式
- GDT 与保护模式
- 虚拟内存初探
- 加载并进入 kernel
- 显示与打印
- 全局描述符表 GDT
- 中断解决
- 虚拟内存欠缺
- 实现堆和 malloc
- 第一个内核线程
- 多线程运行与切换
- 锁与多线程同步
- 进入用户态
- 过程的实现
- 零碎调用
- 简略的文件系统
- 加载可执行程序
- 键盘驱动
- 运行 shell
键盘输入
这个 kernel 系列我的项目到这里曾经实现了所有基本功能的搭建了,最初两篇算是拓展欠缺,咱们将会退出键盘性能,并在此基础上实现一个 shell 命令行界面。
键盘码
键盘的具体原理和实现比拟简短无聊,这里也不想浪费时间解释,感兴趣的话你能够在网上找材料钻研。本篇会尽可能地简单化解决,把很多底层细节省略掉,只关注外围实现原理。
一般来说键盘上你按一个键,而后松开,它会产生两个电信号:
- 按下键产生的那个信号叫通码(
make code
),就是接通的意思; - 松开键产生的那个信号叫断码(
break code
),就是断开的意思;
通码和断码都称为扫描码(scan code
),对于键盘来说是厚此薄彼的,就是发送一个信号给主机而已,操作系统接管到这一系列信号之后,则须要将它们翻译成对应的输入字符。一个键有通、断两个码是必要的,比方在用户界面上你能够决定是按下键就打印出字符,还是肯定要按下并松开才打印字符,这在用户感触上是不一样的;再例如某些组合键,Shift + a,零碎间断接管到 Shfit 的通码和 a 的通码,才会翻译成一个 A 的通码,两头不能够有 Shfit 的断码,否则示意 Shift 曾经松开。
中断触发
键盘的信号是通过中断来触发的,中断号 33,因而咱们首先为它注册中断 handler:
register_interrupt_handler(IRQ1_INT_NUM,
&keyboard_interrupt_handler);
在 keyboard_interrupt_handler 函数里,会从端口 0x60
读取输出的 scan code,而后将它退出到缓冲区暂存。这里咱们用到了一个环形缓冲区(ring buffer
),它是一个容量无限的队列,键盘中断 handler 一直将新输出的 scan code 退出到这个缓冲区尾部,而消费者则从缓冲区头部一直地读取生产 scan code 并翻译成字符。
生产阻塞期待
scan code 缓冲队列消费者是函数 read_keyboard_char_impl,它的外围逻辑在函数 process_scancode,它的性能是将读入的 scan code 翻译为字符。不过它的实现细节不用深究,非常干燥简短,就是对着 scan code 码表翻译而已。
int32 read_keyboard_char_impl() {if (queue.size == 0) {return -1;}
int32 augchar = process_scancode((int)dequeue());
while (!(KH_HASDATA(augchar) && KH_ISMAKE(augchar))) {if (queue.size == 0) {return -1;}
augchar = process_scancode((int)dequeue());
}
return KH_GETCHAR(augchar);
}
如果缓冲区是空的,或者以后的 scan code 不足以翻译成一个无效的字符(例如只读到一个 Shfit 的通码),那么它不会返回无效字符。留神它的退出判断条件:
(KH_HASDATA(augchar) && KH_ISMAKE(augchar))
即翻译进去的是一个无效字符并且是一个通码,就视作是一个非法的按键输出,须要返回给下层做反馈。
read_char 零碎调用
咱们从顶向下来看用户如何获取键盘输入的字符。键盘输入的解决是在 kernel 里的,作为 user 层,须要应用零碎调用来获取键盘输入。咱们定义一个新的零碎调用 read_char,具体实现是 read_keyboard_char 函数,它调用的正是下面的 read_keyboard_char_impl
函数。如果以后无无效字符能被翻译进去,它会阻塞以后线程,那么用户端看来就是程序会卡在这里,期待键盘输入。
如果有新的键盘中断进来,示意有新 scan code,那么 kernel 会唤醒阻塞在 read_keyboard_char
里的期待线程,让它持续生产 scan code 的缓冲区队列,尝试持续翻译无效字符进去。
能够用上面的测试程序,你会在屏幕上失去一个相似 shell 命令行,或者文本编辑器里的按键反馈输入字符的成果:
void test_read_char() {while (1) {int8 c = read_char();
printf("%c", c);
}
}