你的键盘是什么时候生效的?
当你的计算机刚刚启动时,你按下键盘是不生效的,但是过了一段时间后,再按下键盘就有效果了。
那我们今天就来刨根问底一下,到底过了多久之后,按下键盘才有效果呢?
当然首先你得知道,按下键盘后会触发中断,CPU 收到你的键盘中断后,根据中断号,寻找由操作系统写好的键盘中断处理程序。
中断的原理和过程不了解的,可以看我的文章,认认真真的聊聊中断
这个中断处理程序会把你的键盘码放入一个队列中,由相应的用户程序或内核程序读取,并显示在控制台,或者其他用途,这就代表你的键盘生效了。
不过放宽心,我们不展开讲这个中断处理程序以及用户程序读取键盘码后的处理细节,我们把关注点放在,究竟是“什么时候”,按下键盘才会有这个效果。
我们以 Linux 0.11 源码为例,发现进入内核的 main 函数后不久,有这样一行代码。
void main(void) {
...
trap_init();
...
}
看到这个方法的全部代码后,你可能会会心一笑,也可能一脸懵逼。
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。
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);
...
}
#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)
set_trap_gate(0,÷_error);
set_system_gate(5,&overflow);
TIPS:这个 system 与 trap 的区别仅仅在于,设置的中断描述符的特权级不同,前者是 0(内核态),后者是 3(用户态),这块展开将会是非常严谨的、绕口的、复杂的特权级相关的知识,不明白的话先不用管,就理解为都是设置一个中断号和中断处理程序的对应关系就好了。
void trap_init(void) {
...
for (i=17;i<48;i++)
set_trap_gate(i,&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);
...
}
void main(void) {
...
trap_init();
...
tty_init();
...
sti();
...
}
本文可以当做 你管这破玩意叫操作系统源码 系列文章的第 14 回。
为了让不追更系列的读者也能很方便阅读并学到东西,我把它改造成了单独的不依赖系列上下文的文章,具体原因可以看 坚持不下去了...
点击下方的阅读原文可以跳转到本系列的 GitHub 页,那里也有完整目录和规划,以及一些辅助的资料,欢迎提出各种问题。
评论