实验内容
基本内容是修改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,依次切换*,和正常输出,实验完成。