操作系统内核 实验六

实验内容

基本内容是修改Linux 0.11的终端设备处理代码,对键盘输入和字符显示进行非常规的控制.

实验要求

修改内核中I/O设备代码,实现按F12,ls命令显示的信息替换成*,再按F12恢复正常,如此反复。

准备知识

安装虚拟机qemu环境

安装build-essential软件包和qemu

其中build-essential软件包的作用是提供编译程序必须软件包的列表信息.

1
2
sudo apt-get install build-essential
sudo apt-get install qemu

对文件进行编译运行

  1. 输入make编译

  2. 输入make start开始运行

  3. 在虚拟机中按下F12,跳出四个进程信息

  4. 输入ls命令显示文件夹内容

    image-20191125215521786

实验过程

修改kernel/chr_drv/keyboard.S的文件

  1. F12功能

    键盘I/O是典型的中断驱动,在kernel/chr_drv/console.c文件中,控制台进行初始化,代码如下:

    1
    2
    3
    4
    5
    void con_init(void)
    {
    set_trap_gate(0x21,&keyboard_interrupt);
    //键盘中断响应函数设为keyboard_interrupt
    }
  2. 所以每次按键有动作,keyboard_interrupt函数就会被调用,它在文件kernel/chr_drv/keyboard.S

    1
    2
    3
    4
    5
    6
    7
    8
    func:		/*将功能键转化成转义字符存取到读队列中*/
    pushl %eax
    pushl %ecx
    pushl %edx
    call show_stat
    popl %edx
    popl %ecx
    popl %eax
  3. 分析上述代码,会调用show_stat函数,显示当前进程状态,如下图

    image-20191125215443319

  4. call show_stat注释掉,即可屏蔽掉show_sart函数从而实现要实现的目标功能;

    image-20191125215729781

修改kernel/chr_drv/tty_io.c文件

  1. 键盘每次输入一个字符,操作系统都会将这个字符送到字符缓冲区进行处理,F12是一个功能键,它的扫描码是esc,[,[,L ,分别对应ASCII码的27,91,91,76,所以要连续判断四次字符。如果判断这几个字符的时候输出了一个其他的字符,它应该显示这个其他字符, 这段处理程序就应该写在操作系统判断字符做出功能的代码之前。

  2. 修改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文件

  1. tty_io.c文件只是捕捉到了按下F12的状态,还需要在控制程序中添加改变输出的代码,即把所有的字符都变成*

  2. 修改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
    167
    extern 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();
    }

将更改后的文件重新编译

  1. 首先输入命令make clean清空编译;

  2. 输入命令makemake start进行编译并运行;

    image-20191125234858493

    image-20191125235053453

  3. 输入命令ls,发现输出正常;

    image-20191125235327605

  4. 按下F12,输出四个进程;

    image-20191125235516207

  5. 继续输入ls,输出全是*

    image-20191125235710080

  6. 按下F12,输出得全是*;继续输入ls,输出正常。继续测试,按下F12,依次切换*,和正常输出,实验完成。

    image-20191126000059618