实验八 终端设备的控制
实验目的
加深对操作系统设备管理基本原理的认识,实践键盘中断、扫描码等概念;
通过实践掌握 Linux 0.11 对键盘终端和显示器终端的处理过程。
实验内容
本实验的基本内容是修改 Linux 0.11 的终端设备处理代码,对键盘输入和字符显示进行非常规的控制。
在初始状态,一切如常。用户按一次 F12 后,把应用程序向终端输出所有字母都替换为“*”。用户再按一次 F12,又恢复正常。第三次按 F12,再进行输出替换。依此类推。
以 ls 命令为例:
正常情况:
# ls hello.c hello.o hello
第一次按 F12,然后输入 ls:
# ** *****.* *****.* *****
第二次按 F12,然后输入 ls:
# ls hello.c hello.o hello
第三次按 F12,然后输入 ls:
# ** *****.* *****.* *****
实验思路
这里列出其他博主的博客————参考博客
键盘中断初始化
键盘中断的初始化在boot/main.c文件的init()函数中,在这个函数中会调用tty_init()函数对显卡的变量等进行设置(如:video_size_row,vide_mem_start,video_port_reg,video_port_val,video_mem_end等等),并对键盘中断0x21设置,将中断过程指向keyboard_interrupt函数(这是通过set_trap_gate(0x21, &keyboard_interrupt)语句来实现的。
键盘中断发生
当键盘中断发生时,intb $0x60,%al 指令会取出键盘的扫描码,放在al寄存器中,然后查key_table表进行扫描码处理,key_table表中定义了相关所有扫描码对应键的处理函数(如do_self,func,alt,ctrl,none等),比如f1-f12键的处理则要先运行一段处理函数func,调用sched.c中定义的show_stat函数显示当前进程情况,默认情况linux-0.11中所有的功能键都进行这样的动作,然后下一步将控制键以及功能键进行转义,比如ctrl_a将会被转义为^a,f1会被转义为esc[[A,f2被转义为esc[[B,而其他键也经过类似处理,处理完毕后将跳到put_queue,将扫描码放在键盘输入队列中,当然如果没有对应的扫描码被找到,则会通过none标签直接退回,不会被放入队列。然后再调用do_tty_interrupt函数进行最后的处理。do_tty_interrupt直接调用copy_to_cooked函数对队列中的字符进行判断处理,最后调用con_write函数将其输出到显卡内存。在此同时,也会将字符放入辅助队列中,以备缓冲区读写程序使用。
字符输出流程
无论是输出字符到屏幕上还是文件中,系统最终都会调用write函数来进行,而write函数则会调用sys_write系统调用来实现字符输出,如果是输出到屏幕上,则会调用tty_write函数,而tty_write最终也会调用con_write函数将字符输出;如果是输出到文件中,则会调用file_write函数直接将字符输出到指定的文件缓冲区中。所以无论是键盘输入的回显,还是程序的字符输出,那只需要修改con_write最终写到显存中的字符就可以了。但如果要控制字符输出到文件中,则需要修改file_write函数中输出到文件缓冲区中的字符即可。
读队列read_q 用于存放从键盘或串行终端输入的原始(raw)字符序列;写队列 write_q 用于存放写到控制台显示屏或串行终端去的数据;辅助队列 secondary 用于存放经过行规则程序处理(过滤)过的数据,或称为熟(cooked)模式数据。上层终端读函数tty_read用于读取secondary队列中的字符。
对于一个控制台,当用户在键盘上键入了一个字符时,会引起键盘中断响应(中断请求信号 IRQ1, 对应中断号 INT 33 ),此时键盘中断处理程序就会从键盘控制器读入对应的键盘扫描码,然后根据使用的键盘扫描码映射表译成相应字符,放入 tty 读队列 read_q中。然后调用中断处理程序的 C函数 do_tty_interrupt(),它又直接调用行规则函数 copy_to_cooked()对该字符进行过滤处理,并放入 tty 辅助队列 secondary 中,同时把该字符放入tty 写队列 write_q 中,并调用写控制台函数 con_write() 。此时如果该终端的回显( echo )属性是设置的,则该字符会显示到屏幕上。 do_tty_interrupt()和 copy_to_cooked()函数在tty_io.c中实现。
有关控制台终端操作的驱动程序,主要涉及两个程序。一个是键盘中断处理程序 keyboard.S ,主要用于读入用户键入的字符并放入 read_q缓冲队列中;另一个是屏幕显示处理程序 console.c,用于从 write_q队列中取出字符并显示在屏幕上。