关于操作系统:从零开始写-OS-内核-键盘驱动

4次阅读

共计 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);
    }
}
正文完
 0