实验内容
基本内容是修改Linux 0.11的终端设备处理代码,对键盘输入和字符显示进行非常规的控制.
实验要求
修改内核中I/O设备代码,实现按F12,ls命令显示的信息替换成*
,再按F12恢复正常,如此反复。
准备知识
安装虚拟机qemu
环境
安装build-essential
软件包和qemu
包
其中build-essential
软件包的作用是提供编译程序必须软件包的列表信息.
1 | sudo apt-get install build-essential |
对文件进行编译运行
输入
make
编译输入
make start
开始运行在虚拟机中按下F12,跳出四个进程信息
输入
ls
命令显示文件夹内容
实验过程
修改kernel/chr_drv/keyboard.S
的文件
F12功能
键盘I/O是典型的中断驱动,在kernel/chr_drv/console.c文件中,控制台进行初始化,代码如下:
1
2
3
4
5void con_init(void)
{
set_trap_gate(0x21,&keyboard_interrupt);
//键盘中断响应函数设为keyboard_interrupt
}所以每次按键有动作,
keyboard_interrupt
函数就会被调用,它在文件kernel/chr_drv/keyboard.S
中1
2
3
4
5
6
7
8func: /*将功能键转化成转义字符存取到读队列中*/
pushl %eax
pushl %ecx
pushl %edx
call show_stat
popl %edx
popl %ecx
popl %eax分析上述代码,会调用
show_stat
函数,显示当前进程状态,如下图将
call show_stat
注释掉,即可屏蔽掉show_sart
函数从而实现要实现的目标功能;
修改kernel/chr_drv/tty_io.c
文件
键盘每次输入一个字符,操作系统都会将这个字符送到字符缓冲区进行处理,F12是一个功能键,它的扫描码是
esc,[,[,L
,分别对应ASCII
码的27,91,91,76
,所以要连续判断四次字符。如果判断这几个字符的时候输出了一个其他的字符,它应该显示这个其他字符, 这段处理程序就应该写在操作系统判断字符做出功能的代码之前。修改
copy_to_cooked
函数,添加处理程序如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118/* 增加的全局变量 */
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,[,[,L 也会认为是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);
}
修改kernel/chr_drv/console.c
文件
tty_io.c
文件只是捕捉到了按下F12的状态,还需要在控制程序中添加改变输出的代码,即把所有的字符都变成*
;修改
con_write
函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167extern int judge; //引用代码tty_io.c中的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)
//通过判断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<NPAR;npar++)
par[npar]=0;
npar=0;
state=3;
if ((ques=(c=='?')))
break;
case 3:
if (c==';' && npar<NPAR-1) {
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();
}
将更改后的文件重新编译
首先输入命令
make clean
清空编译;输入命令
make
和make start
进行编译并运行;输入命令
ls
,发现输出正常;按下F12,输出四个进程;
继续输入
ls
,输出全是*
;按下F12,输出得全是
*
;继续输入ls
,输出正常。继续测试,按下F12,依次切换*
,和正常输出,实验完成。