GeekOS源代码学习(6)Main函数中Init_Traps 和 Init_Timer 和 Init_Keyboard

回到Main中,看下一个函数Init_Traps()

位于./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 */
}

为中断向量12和13安装了一个中断处理函数GPF_Handler
GPF_Handler函数输出当前寄存器状态,并结束当前线程。
位于/src/geekos/trap.c

/*
 * 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);
}

再看Main中的下一个函数Init_Timer初始化定时器

位于./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);
}

首先的Calbrate_Delay()函数用于测试系统时钟。
Calbrate_Delay位于./src/geekos/timer.c

/*
 * 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();
}

初始化中断函数Timer_Calibrate后,它进入了while()循环,g_numTicks全局变量会在中断函数Timer_Calibrate中增加到CALIBRATE_NUM_TICKS。
看while()后面的Spin(INT_MAX)函数,这是理解的关键。

位于./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)
    );
}

Spin()先设置eax为INT_MAX也就是4B的整型数最大值,为2147483647,然后循环,直到eax为0为止。
Calibrate_Delay中的while循环只是先让时钟中断运行起来。
看一下这个用于校正的中断函数Timer_Calibrate
位于/src/geekos/timer.c

/*
 * 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之后,得到s_spinCountPerTick的值。

流程如下:当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);
}

可以看到,在时钟中断处理函数中增加了当前的全局时钟g_numTicks和当前进程的时钟current->numTicks计数。
如果当前进程的时钟计数达到g_Quantum则标志需要重新调度。
g_currentThread和g_needReschedule均是全局变量。


再看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

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中的所有初始化函数结束,在接下来的学习中,我们会对系统有一个更深入的理解。。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值