GeekOS学习(7)Project0,创建键盘输入进程

终于看完系统的启动代码了!!哈,基本上结构我们也都已经了解了,接下来就开始做project!
先来看第一个,project0,要求创建一个内核线程,打印一些信息,并且调用Wait_For_Key来供用户输入字符。
先找到Wait_For_Key函数来看看

位于./src/geekos/keyboard.c

/*
 * Wait for a keycode to arrive.
 * Uses the keyboard wait queue to sleep until
 * a keycode arrives.
 */
Keycode Wait_For_Key(void)
{
    bool gotKey, iflag;
    Keycode keycode = KEY_UNKNOWN;

    iflag = Begin_Int_Atomic();

    do {
	gotKey = !Is_Queue_Empty();
	if (gotKey)
	    keycode = Dequeue_Keycode();
	else
	    Wait(&s_waitQueue);
    }
    while (!gotKey);

    End_Int_Atomic(iflag);

    return keycode;
}

因为键盘缓冲区属于临界区,所以使用关中断来保护。
gotKey为1则说明键盘缓冲区不空,并从缓冲区出一个键盘码。
gotKey为0则等待。
s_waitQueue是专为键盘缓冲区访问而设置的等待队列,在键盘中断函数中会唤醒此等待队列。


总结,Wait_For_Queue完成:从缓冲区中取数据,每次取一个,缓冲区中若无数据则等待,直到有数据为止。


好了,接下来只要创建一个内核线程并调用Wait_For_Queue就行。


参照Init_Scheduler函数中的Start_Kernel_Thread(Reaper, 0, PRIORITY_NORMAL, true)
来使用Start_Kernel_Thread(print_out, 0, PRIORITY_NORMAL, true)试试看!!
在Main函数上面加入print_out函数

static void print_out(ulong_t arg)
{
	while(1)
	{	
		int key = Wait_For_Key();
		int finish = ('d' | KEY_CTRL_FLAG); 

		if( key == finish ){
			
			Print("finish input \n");
			Exit(0);
		}else
			Print("%c", key);
	}
}

唔,仔细看一下Wait_For_Queue函数就可以写出来了。
另外要注意print_out函数的参数和返回类型。


运行时发现按一个键会打印2个同样的字符,这是因为按下和松开都会产生扫描码,中断处理程序都识别并压入了缓冲区。
好了,project0完成了,^_^!!


让我们来玩一些简单而又更酷的。

试试启动两个进程,一个进程打印A,另一个进程打印B。

添加代码

    Start_Kernel_Thread(print_A, 0, PRIORITY_NORMAL, true);
    Start_Kernel_Thread(print_B, 0, PRIORITY_NORMAL, true);

在Main函数上方添加函数

static void print_A(ulong_t arg)
{
	while(1)
	{
		Print("A");
	}
}
static void print_B(ulong_t arg)
{
	while(1)
	{
		Print("B");
	}
}

重新编译运行试试!

^_^,果然,屏幕一会儿输出A,一会儿输出B。如果你完成了project0的话,你也可以输入字符,这不就是活生生的多任务么。生气

包括内核线程Idle和Reaper。系统里就运行着5个任务。。


下面再来细致的分析一下GeekOS中的多任务机制。

1.时钟中断产生线程切换
任务print_A---->时钟中断---->从通用Handle_Interrupt进入特定中断函数Timer_Interrupt_Handler---->加时钟滴答---->滴答未到期---->返回Handle_Interrupt恢复堆栈回来运行print_A
任务print_A---->时钟中断---->从通用Handle_Interrupt进入特定中断函数Timer_Interrupt_Handler---->加时钟滴答---->滴答到期,置全局标志g_needReschedule表示需要重新调度---->返回Handle_Interrupt调用Get_Next_Runnable将新要运行的线程结构体赋给g_currentThread,并恢复新线程的堆栈---->最后iret返回,运行新进程。
2.键盘中断产生线程切换
任务print_A---->按了一下键位,产生键盘中断---->从通用Handle_Interrupt进入特定中断函数Keyboard_Interrupt_Handler---->处理扫描码得到显示字符keycode,并将keycode放入队列,唤醒等待键盘缓冲区的进程之后,直接置全局标志g_needReschedule表示需要重新调度---->返回Handle_Interrupt调用Make_Runnable将新要运行的线程结构体赋给g_currentThread并恢复新线程的堆栈---->最后iret返回,运行新进程。

你可能感兴趣的:(GeekOS学习(7)Project0,创建键盘输入进程)