实验四linux设备管理,哈工大操作系统实验(五)I/O设备管理

"这篇博客介绍了如何修改Linux0.11内核,使得按下F12键可以实现控制台输出字符的替换。在原始代码中,F12被转换为转义序列[[L。作者通过修改keyboard.S和tty_io.c文件,实现了F12键激活和关闭字符替换功能。当F12被按下,键盘输入的字母会被替换为"*",再次按下则恢复原样。实验涉及中断处理、字符设备驱动和控制台输出的交互。"
摘要由CSDN通过智能技术生成

本次实验内容:

本实验的基本内容是修改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:

**

*****.* *****.* *****

(一)修改linux-0.11/kernel/chr_drv/keyboard.S的文件

首先我们得知道 在修改之前,按下F12什么功能;

键盘I/O是典型的中断驱动,在kernel/chr_drv/console.c文件中:

void con_init(void) //控制台的初始化

{

set_trap_gate(0x21,&keyboard_interrupt); //键盘中断响应函数设为keyboard_interrupt

}

所以每次按键有动作,keyboard_interrupt函数就会被调用,它在文件kernel/chr_drv/keyboard.S

func:

/*程序210行子函数处理功能键

将功能键转化成转义字符存取到读队列中*/

pushl %eax

pushl %ecx

pushl %edx

call show_stat

popl %edx

popl %ecx

popl %eax

调用 show_stat 函数,显示当前 进程状态

屏蔽了 call show_stat的功能再改成我们自己的的功能就好了

func:

/*程序210行子函数处理功能键

将功能键转化成转义字符存取到读队列中*/

pushl %eax

pushl %ecx

pushl %edx

//call show_stat

popl %edx

popl %ecx

popl %eax

(二)修改tty_io.c文件

linux-0.11/kernel/chr_drv/tty_io.c

键盘每次输入一个字符,操作系统都会将这个字符送到字符缓冲区进行处理.

f12是一个功能键,它的扫描码是 esc,[,[,L

分别对应ascii码的27,91,91,76

所以要连续判断四次 字符呀

你说 那我判断这几个字符的时候输出了一个其他的字符,它应该显示这个其他字符啊.

所以这段处理程序就应该写在 操作系统判断字符做出功能的代码之前.

判断字符的代码是copy_to_cooked函数

增加的全局变量

int judge=-1;

int f1=0,f2=0,f3=0;

long j=0;

void copy_to_cooked(struct tty_struct * tty)

{

signed char c;

//now用来判断当前时间戳

long now;

while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {

GETCH(tty->read_q,c);

//开始添加代码

if(c==27)

{

f1=1;

j=jiffies;

//获取当前 函数的CPU心跳数,用来计时

}

else if(f1==1&&f2==0&&c==91)

f2=1;

else if(f1==1&&f2==1&&c==91)

f3=1;

else if(f1==1&&f2==1&&f3==1&&c==76)

{

now=jiffies;

if((now-j)>10)

//比如人为的输入esc,[,[,J 也会认为是F12,所以要根据四个字符的到达的时间判断是一次输入还是多次输入的~

{

printk("%ld \t %ld \n",j,now);

break;

}

judge*=-1;

continue;

}

else

f1=f2=f3=0;

//添加代码结束

if (c==13)

if (I_CRNL(tty))

c=10;

else if (I_NOCR(tty))

continue;

else ;

else if (c==10 && I_NLCR(tty))

c=13;

if (I_UCLC(tty))

c=tolower(c);

if (L_CANON(tty)) {

if (c==KILL_CHAR(tty)) {

/* deal with killing the input line */

while(!(EMPTY(tty->secondary) ||

(c=LAST(tty->secondary))==10 ||

c==EOF_CHAR(tty))) {

if (L_ECHO(tty)) {

if (c<32)

PUTCH(127,tty->write_q);

PUTCH(127,tty->write_q);

tty->write(tty);

}

DEC(tty->secondary.head);

}

continue;

}

if (c==ERASE_CHAR(tty)) {

if (EMPTY(tty->secondary) ||

(c=LAST(tty->secondary))==10 ||

c==EOF_CHAR(tty))

continue;

if (L_ECHO(tty)) {

if (c<32)

PUTCH(127,tty->write_q);

PUTCH(127,tty->write_q);

tty->write(tty);

}

DEC(tty->secondary.head);

continue;

}

if (c==STOP_CHAR(tty)) {

tty->stopped=1;

continue;

}

if (c==START_CHAR(tty)) {

tty->stopped=0;

continue;

}

}

if (L_ISIG(tty)) {

if (c==INTR_CHAR(tty)) {

tty_intr(tty,INTMASK);

continue;

}

if (c==QUIT_CHAR(tty)) {

tty_intr(tty,QUITMASK);

continue;

}

}

if (c==10 || c==EOF_CHAR(tty))

tty->secondary.data++;

if (L_ECHO(tty)) {

if (c==10) {

PUTCH(10,tty->write_q);

PUTCH(13,tty->write_q);

} else if (c<32) {

if (L_ECHOCTL(tty)) {

PUTCH('^',tty->write_q);

PUTCH(c+64,tty->write_q);

}

} else

PUTCH(c,tty->write_q);

tty->write(tty);

}

PUTCH(c,tty->secondary);

}

wake_up(&tty->secondary.proc_list);

}

修改后的tty_io.c文件

/*

* linux/kernel/tty_io.c

*

* (C) 1991 Linus Torvalds

*/

/*

* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles

* or rs-channels. It also implements echoing, cooked mode etc.

*

* Kill-line thanks to John T Kohl.

*/

#include

#include

#include

#define ALRMMASK (1<

#define KILLMASK (1<

#define INTMASK (1<

#define QUITMASK (1<

#define TSTPMASK (1<

#include

#include

#include

#include

#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)

#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)

#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)

#define L_CANON(tty) _L_FLAG((tty),ICANON)

#define L_ISIG(tty) _L_FLAG((tty),ISIG)

#define L_ECHO(tty) _L_FLAG((tty),ECHO)

#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)

#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)

#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)

#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)

#define I_UCLC(tty) _I_FLAG((tty),IUCLC)

#define I_NLCR(tty) _I_FLAG((tty),INLCR)

#define I_CRNL(tty) _I_FLAG((tty),ICRNL)

#define I_NOCR(tty) _I_FLAG((tty),IGNCR)

#define O_POST(tty) _O_FLAG((tty),OPOST)

#define O_NLCR(tty) _O_FLAG((tty),ONLCR)

#define O_CRNL(tty) _O_FLAG((tty),OCRNL)

#define O_NLRET(tty) _O_FLAG((tty),ONLRET)

#define O_LCUC(tty) _O_FLAG((tty),OLCUC)

long j;

struct tty_struct tty_table[] = {

{

{ICRNL, /* change incoming CR to NL */

OPOST|ONLCR, /* change outgoing NL to CRNL */

0,

ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,

0, /* console termio */

INIT_C_CC},

0, /* initial pgrp */

0, /* initial stopped */

con_write,

{0,0,0,0,""}, /* console read-queue */

{0,0,0,0,""}, /* console write-queue */

{0,0,0,0,""} /* console secondary queue */

},{

{0, /* no translation */

0, /* no translation */

B2400 | CS8,

0,

0,

INIT_C_CC},

0,

0,

rs_write,

{0x3f8,0,0,0,""}, /* rs 1 */

{0x3f8,0,0,0,""},

{0,0,0,0,""}

},{

{0, /* no translation */

0, /* no translation */

B2400 | CS8,

0,

0,

INIT_C_CC},

0,

0,

rs_write,

{0x2f8,0,0,0,""}, /* rs 2 */

{0x2f8,0,0,0,""},

{0,0,0,0,""}

}

};

/*

* these are the tables used by the machine code handlers.

* you can implement pseudo-tty's or something by changing

* them. Currently not done.

*/

struct tty_queue * table_list[]={

&tty_table[0].read_q, &tty_table[0].write_q,

&tty_table[1].read_q, &tty_table[1].write_q,

&tty_table[2].read_q, &tty_table[2].write_q

};

void tty_init(void)

{

rs_init();

con_init();

}

void tty_intr(struct tty_struct * tty, int mask)

{

int i;

if (tty->pgrp <= 0)

return;

for (i=0;i

if (task[i] && task[i]->pgrp==tty->pgrp)

task[i]->signal |= mask;

}

static void sleep_if_empty(struct tty_queue * queue)

{

cli();

while (!current->signal && EMPTY(*queue))

interruptible_sleep_on(&queue->proc_list);

sti();

}

static void sleep_if_full(struct tty_queue * queue)

{

if (!FULL(*queue))

return;

cli();

while (!current->signal && LEFT(*queue)<128)

interruptible_sleep_on(&queue->proc_list);

sti();

}

void wait_for_keypress(void)

{

sleep_if_empty(&tty_table[0].secondary);

}

int judge=-1;

int f1=0,f2=0,f3=0;

void copy_to_cooked(struct tty_struct * tty)

{

signed char c;

long now;

while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {

GETCH(tty->read_q,c);

if(c==27)

{

f1=1;

j=jiffies;

}

else if(f1==1&&f2==0&&c==91)

f2=1;

else if(f1==1&&f2==1&&c==91)

f3=1;

else if(f1==1&&f2==1&&f3==1&&c==76)

{

now=jiffies;

if((now-j)>10)

{

printk("%ld \t %ld \n",j,now);

break;

}

judge*=-1;

continue;

}

else

f1=f2=f3=0;

if (c==13)

if (I_CRNL(tty))

c=10;

else if (I_NOCR(tty))

continue;

else ;

else if (c==10 && I_NLCR(tty))

c=13;

if (I_UCLC(tty))

c=tolower(c);

if (L_CANON(tty)) {

if (c==KILL_CHAR(tty)) {

/* deal with killing the input line */

while(!(EMPTY(tty->secondary) ||

(c=LAST(tty->secondary))==10 ||

c==EOF_CHAR(tty))) {

if (L_ECHO(tty)) {

if (c<32)

PUTCH(127,tty->write_q);

PUTCH(127,tty->write_q);

tty->write(tty);

}

DEC(tty->secondary.head);

}

continue;

}

if (c==ERASE_CHAR(tty)) {

if (EMPTY(tty->secondary) ||

(c=LAST(tty->secondary))==10 ||

c==EOF_CHAR(tty))

continue;

if (L_ECHO(tty)) {

if (c<32)

PUTCH(127,tty->write_q);

PUTCH(127,tty->write_q);

tty->write(tty);

}

DEC(tty->secondary.head);

continue;

}

if (c==STOP_CHAR(tty)) {

tty->stopped=1;

continue;

}

if (c==START_CHAR(tty)) {

tty->stopped=0;

continue;

}

}

if (L_ISIG(tty)) {

if (c==INTR_CHAR(tty)) {

tty_intr(tty,INTMASK);

continue;

}

if (c==QUIT_CHAR(tty)) {

tty_intr(tty,QUITMASK);

continue;

}

}

if (c==10 || c==EOF_CHAR(tty))

tty->secondary.data++;

if (L_ECHO(tty)) {

if (c==10) {

PUTCH(10,tty->write_q);

PUTCH(13,tty->write_q);

} else if (c<32) {

if (L_ECHOCTL(tty)) {

PUTCH('^',tty->write_q);

PUTCH(c+64,tty->write_q);

}

} else

PUTCH(c,tty->write_q);

tty->write(tty);

}

PUTCH(c,tty->secondary);

}

wake_up(&tty->secondary.proc_list);

}

int tty_read(unsigned channel, char * buf, int nr)

{

struct tty_struct * tty;

char c, * b=buf;

int minimum,time,flag=0;

long oldalarm;

if (channel>2 || nr<0) return -1;

tty = &tty_table[channel];

oldalarm = current->alarm;

time = 10L*tty->termios.c_cc[VTIME];

minimum = tty->termios.c_cc[VMIN];

if (time && !minimum) {

minimum=1;

if ((flag=(!oldalarm || time+jiffies

current->alarm = time+jiffies;

}

if (minimum>nr)

minimum=nr;

while (nr>0) {

if (flag && (current->signal & ALRMMASK)) {

current->signal &= ~ALRMMASK;

break;

}

if (current->signal)

break;

if (EMPTY(tty->secondary) || (L_CANON(tty) &&

!tty->secondary.data && LEFT(tty->secondary)>20)) {

sleep_if_empty(&tty->secondary);

continue;

}

do {

GETCH(tty->secondary,c);

if (c==EOF_CHAR(tty) || c==10)

tty->secondary.data--;

if (c==EOF_CHAR(tty) && L_CANON(tty))

return (b-buf);

else {

put_fs_byte(c,b++);

if (!--nr)

break;

}

} while (nr>0 && !EMPTY(tty->secondary));

if (time && !L_CANON(tty)) {

if ((flag=(!oldalarm || time+jiffies

current->alarm = time+jiffies;

else

current->alarm = oldalarm;

}

if (L_CANON(tty)) {

if (b-buf)

break;

} else if (b-buf >= minimum)

break;

}

current->alarm = oldalarm;

if (current->signal && !(b-buf))

return -EINTR;

return (b-buf);

}

int tty_write(unsigned channel, char * buf, int nr)

{

static int cr_flag=0;

struct tty_struct * tty;

char c, *b=buf;

if (channel>2 || nr<0) return -1;

tty = channel + tty_table;

while (nr>0) {

sleep_if_full(&tty->write_q);

if (current->signal)

break;

while (nr>0 && !FULL(tty->write_q)) {

c=get_fs_byte(b);

if (O_POST(tty)) {

if (c=='\r' && O_CRNL(tty))

c='\n';

else if (c=='\n' && O_NLRET(tty))

c='\r';

if (c=='\n' && !cr_flag && O_NLCR(tty)) {

cr_flag = 1;

PUTCH(13,tty->write_q);

continue;

}

if (O_LCUC(tty))

c=toupper(c);

}

b++; nr--;

cr_flag = 0;

PUTCH(c,tty->write_q);

}

tty->write(tty);

if (nr>0)

schedule();

}

return (b-buf);

}

/*

* Jeh, sometimes I really like the 386.

* This routine is called from an interrupt,

* and there should be absolutely no problem

* with sleeping even in an interrupt (I hope).

* Of course, if somebody proves me wrong, I'll

* hate intel for all time :-). We'll have to

* be careful and see to reinstating the interrupt

* chips before calling this, though.

*

* I don't think we sleep here under normal circumstances

* anyway, which is good, as the task sleeping might be

* totally innocent.

*/

void do_tty_interrupt(int tty)

{

copy_to_cooked(tty_table+tty);

}

void chr_dev_init(void)

{

}

(三)修改linux-0.11/kernel/chr_drv/console.c

tty_io.c文件只是捕捉到了按下f12的状态

我们需要在控制程序中添加 改变输出的代码,最简单的方法是只要按下F12就把所有的字符都变成"*"号输出就行了

我们只要在con_write()函数中添加一行代码:

extern int judge;//引用代码tty_io.c中的judge变量

if(judge==1)

c='*';

con_write函数:

extern int judge;

void con_write(struct tty_struct * tty)

{

int nr;

char c;

nr = CHARS(tty->write_q);

while (nr--) {

GETCH(tty->write_q,c);

switch(state) {

case 0:

if (c>31 && c<127) {

if (x>=video_num_columns) {

x -= video_num_columns;

pos -= video_size_row;

lf();

}

if(judge==1)

c='*';

__asm__("movb attr,%%ah\n\t"

"movw %%ax,%1\n\t"

::"a" (c),"m" (*(short *)pos)

);

pos += 2;

x++;

} else if (c==27)

state=1;

else if (c==10 || c==11 || c==12)

lf();

else if (c==13)

cr();

else if (c==ERASE_CHAR(tty))

del();

else if (c==8) {

if (x) {

x--;

pos -= 2;

}

} else if (c==9) {

c=8-(x&7);

x += c;

pos += c<<1;

if (x>video_num_columns) {

x -= video_num_columns;

pos -= video_size_row;

lf();

}

c=9;

} else if (c==7)

sysbeep();

break;

case 1:

state=0;

if (c=='[')

state=2;

else if (c=='E')

gotoxy(0,y+1);

else if (c=='M')

ri();

else if (c=='D')

lf();

else if (c=='Z')

respond(tty);

else if (x=='7')

save_cur();

else if (x=='8')

restore_cur();

break;

case 2:

for(npar=0;npar

par[npar]=0;

npar=0;

state=3;

if ((ques=(c=='?')))

break;

case 3:

if (c==';' && npar

npar++;

break;

} else if (c>='0' && c<='9') {

par[npar]=10*par[npar]+c-'0';

break;

} else state=4;

case 4:

state=0;

switch(c) {

case 'G': case '`':

if (par[0]) par[0]--;

gotoxy(par[0],y);

break;

case 'A':

if (!par[0]) par[0]++;

gotoxy(x,y-par[0]);

break;

case 'B': case 'e':

if (!par[0]) par[0]++;

gotoxy(x,y+par[0]);

break;

case 'C': case 'a':

if (!par[0]) par[0]++;

gotoxy(x+par[0],y);

break;

case 'D':

if (!par[0]) par[0]++;

gotoxy(x-par[0],y);

break;

case 'E':

if (!par[0]) par[0]++;

gotoxy(0,y+par[0]);

break;

case 'F':

if (!par[0]) par[0]++;

gotoxy(0,y-par[0]);

break;

case 'd':

if (par[0]) par[0]--;

gotoxy(x,par[0]);

break;

case 'H': case 'f':

if (par[0]) par[0]--;

if (par[1]) par[1]--;

gotoxy(par[1],par[0]);

break;

case 'J':

csi_J(par[0]);

break;

case 'K':

csi_K(par[0]);

break;

case 'L':

csi_L(par[0]);

break;

case 'M':

csi_M(par[0]);

break;

case 'P':

csi_P(par[0]);

break;

case '@':

csi_at(par[0]);

break;

case 'm':

csi_m();

break;

case 'r':

if (par[0]) par[0]--;

if (!par[1]) par[1] = video_num_lines;

if (par[0] < par[1] &&

par[1] <= video_num_lines) {

top=par[0];

bottom=par[1];

}

break;

case 's':

save_cur();

break;

case 'u':

restore_cur();

break;

}

}

}

set_cursor();

}

(四)大功告成,编译运行把~

报告:

(1)在原始代码中,按下F12,中断响应后,中断服务程序会调用func?它实现的是什么功能?

将F12转义成转义字符序列 [ [ L , 对F1-F12处理类似 [ [ A -> [ [ L

(2) 在你的实现中,是否把向文件输出的字符也过滤了?如果是,那么怎么能只过滤向终端输出的字符?如果不是,那么怎么能把向文件输出的字符也一并进行过滤?

实现中并没有把向文件输出的字符也过滤,在copy_to_cooked函数处理中并不是将字符放到write_q,然后调用con_write来显示到控制台。

而是shell通过上层的sys_write系统调用将tty->secondary队列中的字符显示出来的。而在sys_write的实现过程中调用了tty_write函数。

所以只修改tty_write后,按键回显当然也变成’*’,第二次按的时候回复原来的样子;

因此要想将向文件输出的字符一并进行过滤,需要修改file_write函数。设置Flag,如果为F12按下状态,将从内核态读出的数据转为‘*’赋给用户空间即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值