linux 0.11 源码学习(十三)

字符设备驱动

在linux 0.11版本中,字符设备驱动源码分为三块:向上暴露的字符设备驱动接口tty_io.c与tty_ioctl.c,与块设备驱动一样,在字符驱动文件(read_write.c)操作中被调用;实现串口的驱动rs_io.s和serial.c;实现键盘驱动的keyboard.s以及实现控制台输出的console.c。

对于字符设备驱动linux为每个设备维护一个tty_struct的结构体,在这个结构体内部包含了字符设备的读写缓冲队列,核心的几个结构体定义如下:

struct tty_struct {
    struct termios termios;//驱动设备属性
    int pgrp;
    int stopped;
    void (*write)(struct tty_struct * tty);//写函数指针,具体设备的写处理,会在tty_write中被调用!
    struct tty_queue read_q;
    struct tty_queue write_q;
    struct tty_queue secondary;//tty辅助队列,存放将read_q中数据经过行规则处理后的cooked数据。
    };

extern struct tty_struct tty_table[];
struct tty_queue {
    unsigned long data;
    unsigned long head;
    unsigned long tail;
    struct task_struct * proc_list;//等待本队列的进程列表
    char buf[TTY_BUF_SIZE];//缓存区长度为1024
};
struct termios {
    unsigned long c_iflag;        /* input mode flags   */
    unsigned long c_oflag;        /* output mode flags  */
    unsigned long c_cflag;        /* control mode flags */
    unsigned long c_lflag;        /* local mode flags   */
    unsigned char c_line;         /* line discipline    */
    unsigned char c_cc[NCCS];     /* control characters */ //可以更改的特殊字符
};

注<参考UNIX环境高级编程 十八章 终端I/O>:在unix系统中遵循的POSIX.1标准,终端I/O有两种不同的工作模式:

  1. 规范模式输入处理:终端以行为单位进行处理,对每个读要求驱动最多返回一行;处理流程用户进程 -> 读写函数 -> 终端行规则 -> 终端设备驱动程序 -> 实际终端设备
  2. 非规范模式输入处理:输入字符不组成行;

在默认情况下是规范模式。其次所有的终端设备特性都包含在terminos结构中。

 在tty_io.c中主要是三个函数copy_to_cooked、tty_read和tty_wirte。这里tty_read和tty_write都是被用户进程调用,copy_to_cooked被中断处理do_tty_interrupt调用。do_tty_interrupt在具体的设备中断中被调用,见keyboard.s和rs_io.s。

其中copy_to_cooked函数实现的就是上面注释中终端行规则处理,它将read_q的数据处理后写入secondary。

  • void copy_to_cooked(struct tty_struct * tty)
    • 循环遍历输入队列,完成如下几件事:
      • 处理是否将回车符转换为换行符;
      • 处理是否将输入的大写转换成小写;
      • 如果开启规范模式输入,则处理键盘终止符、擦除字符、回显符等;
      • 处理字符INTR和QUIT,调用tty_intr产生相应的信号;即更新task的mask;
      • 如果是回显输入字符,则放入输出队列write_q;
      • 将处理完的数据放入secondary队列
    • 唤醒secondary中的等待进程,即设置状态为runnable;
void wake_up(struct task_struct **p)//疑问:明明是进程列表,但这里貌似只唤醒了对头的进程??
{
    if (p && *p) {
        (**p).state=0;
        *p=NULL;
    }
}

 tty_read和tty_write所完成的工作相对比较简单,从sencondary中读取数据,或向write_q中写入数据。对tty_write在写入数据后调用schedule触发调度。

posted @ 2013-05-11 15:04  Fredric_2013  阅读(552)  评论(0编辑  收藏  举报