HIT oslab之设备管理 (显示器 + 键盘)

一、整体逻辑

根据unix/linux哲学:一切皆文件,所以设备也有设备文件。操作系统要让用户像操作文件一样去使用终端设备,即实现了设备管理。

以下内容的操作系统指linux-0.11

二、操作系统管理显示器的过程

所谓操作系统管理显示器,即让用户通过printf("Hello world!\n");即可在屏幕上打印出Hello world!

1.printf函数中最核心的2件事情

(1)创建缓存buf, 存储格式化输出

此处指存储Hello world!\n

(2)write(1, buf, ...)
1)write根据文件描述符(这里是1),把内容输出到对应的设备上。
2)1对应的就是dev/tty0, tty0指的就是终端设备文件。

解释:
①1号进程执行init函数,安装根文件系统,接着打开了/dev/tty0,并拷贝了2份,所以文件描述符0~2对应的都是是dev/tty0。
在这里插入图片描述
②tty0是终端设备文件,且是字符设备文件(由crw-rw-rw-的c可知)
在这里插入图片描述

2.write函数

引发系统调用中断,经处理系统调用中断后,跳转到其对应的系统调用函数sys_write执行

3.sys_write函数

(1)找到文件描述符对应的文件结构
在这里插入图片描述
(2)取该文件的信息(信息说明了该设备的属性,比如可能是字符设备文件)
在这里插入图片描述
(3)根据不同的属性,调用相应的写操作函数。
在这里插入图片描述

显示器是字符设备。

4.rw_char函数

inode->i_zone[],即设备号指示哪个字符设备。并且是WRITE字符。

本例是i_zone[0],根据上文指出了tty0是字符设备,且由图可知设备号是4。

5.根据dev = 4, 找到其处理函数

在这里插入图片描述

这里和系统调用的逻辑很像,根据系统调用号,找到对应的系统调用函数。而且也是一个函数指针构成的数组。

crw_table[4],即rw_ttyx
在这里插入图片描述

6.rw_ttyx函数

在这里插入图片描述

由于rw == WRITE,所以执行tty_write函数

7.tty_write函数,实现输出的核心函数

(1)每个tty设备有3个缓冲队列,分别是读缓冲队列(read_q)、写缓冲队列(write_q)和辅助缓冲队列(secondary)。

此处先介绍write_q;

(2)write_q : 利用缓冲技术解决CPU和外设速度不匹配问题
1)如果缓冲区满了,那么就要sleep了
在这里插入图片描述
2)如果缓冲区未满,那么就做以下2步:
①c = get_fs_byte(b); 从用户缓冲区读1个字符。

涉及到内核段获取用户段数据的知识

②PUTCH(c, tty->write_q); 把字符放入写缓冲区中;

3)之后,才是真的开始输出屏幕了,tty->write(tty);

8.tty->write的write是tty这个结构体的成员函数,要揭开其真面目,得看tty这个结构体的初始化

在这里插入图片描述
在这里插入图片描述

tty_struct在kernel/chr_drv/tty_io.c中初始化,可以看到,write的真面目就是con_write

9.con_write函数(从写缓冲区读数据,也就是“消费者”)

kernel/chr_drv/console.c中

(1)GETCH(tty->write_q, c); 从写缓冲区读1个字符。

(2)向显示器输出
在这里插入图片描述
①ah存属性值,al存字符c
mov ax, pos pos即显卡的寄存器

统一寻址,用mov;独立寻址,用out。

到此为止,屏幕上就出现了Hello World!的H

10.总结

要想实现让用户仅仅通过printf语句就能够向屏幕输出字符串,则操作系统要完成如下的封装:
①向某个文件描述符write要输出的字符串;

②产生系统调用中断,sys_write根据文件描述符就知道是什么类型的设备,而显示器是字符设备,所以转去rw_char函数;

③字符设备也有很多种,由设备号来标识。所以rw_char函数根据设备号,找到其对应的处理函数rw_ttyx函数。

④rw_ttyx函数根据操作类型(读 or 写)执行不同的函数,如果是写,则执行tty_write函数。

⑤tty_write函数调用con_write函数向显示器的显卡寄存器写入字符。

三、操作系统管理键盘的过程

所谓操作系统管理键盘,即用户按下键盘的某个键,比如a,则在屏幕上显示a。

1.当用户按下键盘的某个键时,引起键盘中断。在这里插入图片描述
2.处理键盘中断的程序:keyboard_interrupt,以下为该程序实现的主要功能
(1)从端口0x60,读扫描码inb $0x60,%al

inb : 读入1个字节

(2)根据扫描码,调用对应扫描码处理子程序; call key_table(,%eax,4)
在这里插入图片描述

大部分的按键都是执行do_self子程序

(3)do_self子程序的核心功能:
根据扫描码取对应的ASCII码 ,call put_queue,即放入读字符队列(read_q)。

(4)call do_tty_interrupt(该函数调用了copy_to_cooked)
copy_to_cooked主要作用:把read_q读缓存队列中的字符处理后放入规范模式队列(辅助队列secondary)中

到此为止,键盘输入已经完成。接下来,就是回显,即在屏幕上显示a

3.若设置了回显标志,那么copy_to_cooked还需要将字符放入写队列write_q
在这里插入图片描述

而结合上文,在con_write函数中,便会从写队列write_q取出字符,并向显示器输出。

到此为止,用户按下键盘的某个键,比如a,则在屏幕上显示a。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值