Chinaunix首页 | 论坛 | 博客
  • 博客访问: 469849
  • 博文数量: 164
  • 博客积分: 4024
  • 博客等级: 上校
  • 技术积分: 1580
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-10 16:27
文章分类

全部博文(164)

文章存档

2011年(1)

2010年(108)

2009年(55)

我的朋友

分类:

2010-06-28 14:04:31

【输入输出系统】

在学习输入输出之前还是回顾一下系统API与进程的优先级问题:

×系统API

进程在调用API的时候也需要进行特权级转换,这个跟切换进程表没啥两样。所以定义一个API的主要步骤可以是(这里指的的宏内核):

在IDT增加一个自定义的向量号并初始化成中断门描述符。中断处理函数基址假设是sys_call.(该函数在Kernel.asm属于内核),指定s描述符属性为DPL=3,最低特权,这样用户就可以访问它了。

sys_call:                        

                     call    save     ;保存寄存器并且检查重入。

                       sti             ;由于是INT 软中断,所以不需要控制8259A

                       call        [sys_call_table + eax * 4]                    ;eax是功能号

                      mov       [esi + EAXREG - P_STACKBASE],eax ;将eax返回到进程表里去

                       cli

sys_call_table是一个函数数组的基址,这些函数可以用C语言写.可以看到这个中断号不此一个API还可以根据传来的参数选择调用其他内核函数。那么接下来就写触发API的函数:

get_api:               //用汇编代码写。C语言调用就行了。

              mov       eax,0

                 int     0x90             ;就是这个中断触发sys_call 然后根据eax 选择API。

×优先级

解决进程的优先级:在先前的时钟中断处理中,我们也就是简单的将p_proc_ready指向下一个进程表。每个进程都是公平的,那么现在就来具体说说实现优先级,在时钟中断有一个全局变量是ticks;每10ms会自动加1,那么我们就根据ticks的方式来计算我们的优先级。进程的执行顺序是从进程表基址开始的,但是ticks越大代表进程需要处理的次数越多。

可以在进程表加入两个字段:ticks(计数器)、priority(优先级) 它们的值一样;

假如有3个进程:

ticks_1=priority=150;

ticks_2=priority=50;

ticks_3=priority=30;

那么优先级是怎么来计算的呢?优先级调度:

(在时钟中断代码处,p_proc_ready->ticks--;表示当前运行的进程优先级减减)

for(p=proc_table;p<=proc_table+ NR_TASKS;p++) //用一个循环遍历所有进程表

{

                 //找出进程表中ticks值最大的那个,找到后将它指定为要运行的进程.

                  if(p->ticks > greatest_ticks)

                     {

                             greatest_ticks=p->ticks;

                             p_prco_ready =p ;

                    }

//这就是一个优先级别调度的核心代码,其作用就是比较进程表里所有的ticks.这里的进程分别是A=150,B=50,C=30.,由于比较是安顺序的,所以A在前面的100次ticks都是独立运行的,直到50ticks的时候,A开始与B同步运行,再减到30的时候A、B、 C都同步运行了. 如果在A、B、C三个进程循环输出都延迟200ms,也就是20ticks。那么可以这样求出三个进程共运行了多少次循环:

A= 100/20 + (50-30)*2/20 +30*3/20 =11次

B= 0/20 + (50-30)*2/20 +30*3/20 =6次          //0表示没有参与

C= 0/20 + 0/20 +30*3/20 =4次

}优先级调度主要靠进程的ticks来实现。当ticks最大时,那么就是一直是此进程运行。如果相等那么就会同步运行..想要进程绝对同步(进程一个一个运行)的话。只需要在中断处理开始的时候:

if(p_proc_ready->ticks>0)return ;//这样就代表当前进程的ticks还没有减完的不会去切换进程.

在不是绝对同步的情况下。那么所有进程只要在ticks相等后就会一起运行了。

一、键盘

接收键盘中断的IRQ就是8259A主片的IRQ1,当今最流行的键盘分别是USB和PS/2。

键盘中存在着一枚叫做键盘编码器(Keyboard Encoder)的芯片,它通常是Intel 8048以及兼容芯片.作用是监视键盘的输入。并把适当的数据传送给计算机。另外在计算机主板上还有一个键盘控制器8042(keyboard controller),用来接收和解码来自键盘的数据,并与8259A以及软件等进行通信。那么一个键盘信息获取流程就是:

8048encoder(监视到键盘动作后发送扫描码)--->8042controller(转换成scan code set1,并放置到输入缓冲区)--->告之8259A已经产生了IRQ1中断,此时8042将不再接收扫描码,直到缓冲区被清空才会收到更多的扫描码.那么我们就必需先处理输入缓冲区才能让8042继续接收扫描码。

8024有3个寄存器,其大小都是1个字节:输出缓冲区(0x60)、输入缓冲区(0x60)、状态寄存器(0x64)、控制寄存器(0x64)、 可以看到就两个端口号,需要用in、out两种命令进行读取访问。

若想要获取键盘的具体信息那就需要根据scan_code_set1这个扫描码表格去判断了,这里处理组合键有点复杂!!!扫描码的长度不是统一的这就需要定义一个缓冲区来存放扫描码。然后再将它转换成ASCII码或者功能键。

键盘缓冲区可以这样:

typedef   s_kb{

               char*     p_head; 缓冲区的头部,用来指示空闲的字节位置,自增指针。

               char*     p_tial;指向键盘任务应处理的字节,通常是tial---head这段就是当前要处理的扫描码.

        int length;           缓冲区当前扫描码数据有多少字节

          char*        buf[NR_KB_SIZE];//缓冲区

};KB_INPUT;

那么现在就可以在键盘IRQ1(8259A)中用8042接收扫描码并且读出输入缓冲区的数据到KB_INPUT,然后在对照scan_code_set1表格分析,得到合适的键盘信息!!!

二、TTY(终端)

俗称显示器,其实它叫做终端。跟其他的硬件一样,也需要进行端口的操作.只是TTY把结果用可视化展现给人看而已!!!

那么你肯定想到了,就是操作寄存器了。TTY的寄存器组,这里就用VGA模式的来说明:

VGA寄存器非常之多不过操作端口就一个0x3d5(CRT Controller Register).访问寄存器的方式需要两端口操作:

out      0x3d4,idex        ;这一步是确定0x3d5操作的索引

out    0x3d5,new_value;这一步输出到索引寄存器中.

80×25模式下的显示器,显存有32KB。基地址是b8000,那么一个屏幕需要4KB.这就代表该显存能存放8个屏幕的内容。这就可以让我们的进程切换显存的基址,来达到终端的效果。也就是一个进程对应一个显存的基地址。不同进程的读写当然也是以显存基址为标准了。

TTY,VGA模式的操作也就是简单的两项:不过它的索引涉及到了很多内容比如光标、显存基址。

三、C语言的Printf

C函数库里面的一个函数,先看看函数原型:

int printf(
    const char* [, ]...
);

wsprintf(char *buf ,const *fmt,va_list args)     //va_list是无类型的一个变量并且最后参数是可变参数。
C函数的参数压栈是第一个参数最后压栈,那么fmt就是在参数的低地址下面。那么再调用函数后.fmt的esp是确定的再往上就是参数了。那么 用fmt的堆栈地址+4就可以间接访问到传进来的参数。若要确定参数的个数那么在调用函数之前。记下栈的位置就可以了!!!!这个函数是将后面的参数格式化输出到缓冲区:

printf()原理是一样的。只不过它输出到显存。

【总结】

硬件与软件。

从8259A --->8253(时钟芯片)--->8048(KerboardCode)键盘编码芯片(监视键盘)--->8042(keyboardController)键盘控制器,接收8048发送的scan_code_set1--->TTY(终端).

其大部分是对端口的I/O操作.

从引导程序-Boot.bin-->Loader.bn-->kernel.bin内核--->进程--->优先级--->I/O.

其大部分是围绕CPU的保护模式。。当然这里还引用了很多自主的逻辑。如进程表、进程调度、优先级调度,而这是自主的逻辑又必需遵循CPU的规则。应该这么说这些自主的逻辑都是从保护模式的规则里提炼抽取出来的。

好了。。放松一下.............

阅读(1314) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~