您的位置:首页 > 其它

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

2012-04-13 22:41 579 查看
终于看完系统的启动代码了!!哈,基本上结构我们也都已经了解了,接下来就开始做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返回,运行新进程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐