位于./src/geekos/trap.c
/* * Initialize handlers for processor traps. */ void Init_Traps(void) { Install_Interrupt_Handler(12, &GPF_Handler); /* stack exception */ Install_Interrupt_Handler(13, &GPF_Handler); /* general protection fault */ }
/* * Handler for general protection faults and other bad errors. * Kill the current thread (which caused the fault). */ static void GPF_Handler(struct Interrupt_State* state) { /* Send the thread to the reaper... */ Print("Exception %d received, killing thread %p\n", state->intNum, g_currentThread); Dump_Interrupt_State(state); Exit(-1); /* We will never get here */ KASSERT(false); }
位于./src/geekos/timer.c
void Init_Timer(void) { /* * TODO: reprogram the timer to set the frequency. * In bochs, it defaults to 18Hz, which is actually pretty * reasonable. */ Print("Initializing timer...\n"); /* Calibrate for delay loop */ Calibrate_Delay(); Print("Delay loop: %d iterations per tick\n", s_spinCountPerTick); /* Install an interrupt handler for the timer IRQ */ Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler); Enable_IRQ(TIMER_IRQ); }
/* * Calibrate the delay loop. * This will initialize s_spinCountPerTick, which indicates * how many iterations of the loop are executed per timer tick. */ static void Calibrate_Delay(void) { Disable_Interrupts(); /* Install temporarily interrupt handler */ Install_IRQ(TIMER_IRQ, &Timer_Calibrate); Enable_IRQ(TIMER_IRQ); Enable_Interrupts(); /* Wait a few ticks */ while (g_numTicks < CALIBRATE_NUM_TICKS) ; /* * Execute the spin loop. * The temporary interrupt handler will overwrite the * loop counter when the next tick occurs. */ Spin(INT_MAX); Disable_Interrupts(); /* * Mask out the timer IRQ again, * since we will be installing a real timer interrupt handler. */ Disable_IRQ(TIMER_IRQ); Enable_Interrupts(); }
位于./src/geekos/timer.c
/* * Delay loop; spins for given number of iterations. */ static void Spin(int count) { /* * The assembly code is the logical equivalent of * while (count-- > 0) { // waste some time } * We rely on EAX being used as the counter * variable. */ int result; __asm__ __volatile__ ( "1: decl %%eax\n\t" "cmpl $0, %%eax\n\t" "nop; nop; nop; nop; nop; nop\n\t" "nop; nop; nop; nop; nop; nop\n\t" "jg 1b" : "=a" (result) : "a" (count) ); }
/* * Temporary timer interrupt handler used to calibrate * the delay loop. */ static void Timer_Calibrate(struct Interrupt_State* state) { Begin_IRQ(state); if (g_numTicks < CALIBRATE_NUM_TICKS) ++g_numTicks; else { /* * Now we can look at EAX, which reflects how many times * the loop has executed */ /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/ s_spinCountPerTick = INT_MAX - state->eax; state->eax = 0; /* make the loop terminate */ } End_IRQ(state); }
流程如下:当g_numTicks==CALIBRATE_NUM_TICKS时,Calibrate_Delay函数中的循环退出,开始Spin()递减eax,过了一个tick之后,时钟中断触发,再时钟中断函数里看减了几次eax,以此来估计一次时钟中断的时间间隔。
随后才安装了真正的时钟中断函数Timer_Interrupt_Handler
时钟中断为0号外部中断,中断向量为32。
位于src/geekos/timer.c
static void Timer_Interrupt_Handler(struct Interrupt_State* state) { struct Kernel_Thread* current = g_currentThread; Begin_IRQ(state); /* Update global and per-thread number of ticks */ ++g_numTicks; ++current->numTicks; /* * If thread has been running for an entire quantum, * inform the interrupt return code that we want * to choose a new thread. */ if (current->numTicks >= g_Quantum) { g_needReschedule = true; } End_IRQ(state); }
再看Main中最后一个初始化函数!Init_Keyboard
位于./src/geekos/keyboard.c
void Init_Keyboard(void) { ushort_t irqMask; Print("Initializing keyboard...\n"); /* Start out with no shift keys enabled. */ s_shiftState = 0; /* Buffer is initially empty. */ s_queueHead = s_queueTail = 0; /* Install interrupt handler */ Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler); /* Enable IRQ1 (keyboard) */ irqMask = Get_IRQ_Mask(); irqMask &= ~(1 << KB_IRQ); Set_IRQ_Mask(irqMask); }
Keyboard_Interrupt_Handler位于./src/geekos/keyboard.c
/* * Handler for keyboard interrupts. */ static void Keyboard_Interrupt_Handler(struct Interrupt_State* state) { uchar_t status, scanCode; unsigned flag = 0; bool release = false, shift; Keycode keycode;//Keycode是两个字节 Begin_IRQ(state); status = In_Byte(KB_CMD); IO_Delay(); if ((status & KB_OUTPUT_FULL) != 0) { /* There is a byte available */ scanCode = In_Byte(KB_DATA);//得到扫描码,按下键和松开键都会产生扫描码 IO_Delay(); /* * Print("code=%x%s\n", scanCode, (scanCode&0x80) ? " [release]" : ""); */ if (scanCode & KB_KEY_RELEASE) {//处理键盘松开时产生的扫描码 release = true; scanCode &= ~(KB_KEY_RELEASE); } if (scanCode >= SCAN_TABLE_SIZE) { Print("Unknown scan code: %x\n", scanCode); goto done; } /* Process the key */ shift = ((s_shiftState & SHIFT_MASK) != 0);//s_shiftState 是全局的变量,用于保存上一个按下的功能键 keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode]; /* Update shift, control and alt state */ switch (keycode) { case KEY_LSHIFT: flag = LEFT_SHIFT;//flag标志用于记录其他功能键 break; case KEY_RSHIFT: flag = RIGHT_SHIFT; break; case KEY_LCTRL: flag = LEFT_CTRL; break; case KEY_RCTRL: flag = RIGHT_CTRL; break; case KEY_LALT: flag = LEFT_ALT; break; case KEY_RALT: flag = RIGHT_ALT; break; default: goto noflagchange; } //代码运行到这里说明扫描码是属于功能键 if (release)//是松开而产生的扫描码,比如说松开ctrl键 s_shiftState &= ~(flag); else s_shiftState |= flag;//保存标志 //功能键松开和按下的扫描码就是flag那一位的不同 /* * Shift, control and alt keys don't have to be * queued, flags will be set! */ goto done; noflagchange: /* Format the new keycode */ if (shift) keycode |= KEY_SHIFT_FLAG; if ((s_shiftState & CTRL_MASK) != 0)//处理功能键按下的字符。 keycode |= KEY_CTRL_FLAG; if ((s_shiftState & ALT_MASK) != 0) keycode |= KEY_ALT_FLAG; if (release) keycode |= KEY_RELEASE_FLAG; /* Put the keycode in the buffer */ Enqueue_Keycode(keycode); /* Wake up event consumers */ Wake_Up(&s_waitQueue);//唤醒在队列上等待读取缓冲区的进程 /* * Pick a new thread upon return from interrupt * (hopefully the one waiting for the keyboard event) */ g_needReschedule = true;//标记全局变量,让系统进行任务调度 } done: End_IRQ(state); }
通过一系列的对功能键的处理和判断,最后得到keycode,放入键盘缓冲区中。
至此,Main中的所有初始化函数结束,在接下来的学习中,我们会对系统有一个更深入的理解。。