概述
TTY终端的输入过程 ================= 1) 当用户按压键盘时, 键盘中断处理程序将经过转换的键盘功能码用tty_insert_flip_char()放入到当前打开终端的翻转缓冲区之中, 然后将缓冲区输出任务函数(flush_to_ldisc)添加到控制台任务队列(con_task_queue)并激活控制台软中断执行该任务函数. flush_to_ldisc()翻转读写缓冲区, 将缓冲区接收数据传递给tty终端规程的n_tty_receive_buf()接收函数, n_tty_receive_buf()处理输入字符, 将输出字符缓冲在终端的循环缓冲区(read_buf)之中. 用户通过tty规程的read_chan()读取read_buf中的字符. 当多个进程同时读取同一终端时, 使用tty->atomic_read信号灯来竞争读取权. 2) 每一打开的终端接口中包含终端的参数结构(termios), 当终端打开时, 它将继承终端驱动设备参数表中参数结构, 系统缺省的终端参数为tty_std_termios, 驱动设备的初始终端参数为init_termios. 终端参数可由用户通过ioctl()调整, 用来控制终端接口的行为. c_iflag描述对输入字符的识别模式, c_oflag描述对输出字符的处理模式, c_cflag描述终端接口的控制模式, c_lflag描述字符处理过程中各种本地行为, 还有由19个字符组成的c_cc字符参数表, 描述各类控制字符以及辅助参数. 3) 当本地模式具有规范输入标志(ICANON)时, 输出数据按行拷贝给用户. 在接收到换行符(或者文件结束符c_cc[VEOF],行结束符c_cc[VEOL]和c_cc[VEOL2])之前, 可以使用编辑控制符完成简单的编辑. c_cc[VERASE]删除前一字符, c_cc[VKILL]删除整行, c_cc[VWERASE]删除前一单词. 4) 当本地模式具有回显标志(ECHO)时, 输入字符按一定的规则回显到输出设备上. 当本地模式具有信号标志(ISIG)时, 特定的输入字符可以在终端当前进程组中产生信号. c_cc[VINTR]产生中断信号, c_cc[VQUIT]产生退出信息, c_cc[VSUSP]产生暂停信号. c_cc[VTIME]定义了用户读取数据的等待超时, 单位为0.1秒, c_cc[VMIN]为所期望的字节数. 在非ICANON模式下, 如果在c_cc[VTIME]时间内接收到c_cc[VMIN]数量的字符, 读过程返回. c_cc[VSTOP]暂停驱动设备输出, c_cc[VSTART]启动驱动设备输出. 5) 内核初始化结束后, 打开控制台终端设备/dev/console作为输入输出去运行/sbin/init. /dev/console的设备号是0x0501, 在打开时被定向到命令行(console=)指定的终端设备, 缺省情况下为/dev/tty1, 其设备号为0x0401. init程序完成用户初始化完成后启动若干个终端守护程序(getty). getty在打开所守护的终端文件之前先使用setsid()创建会话组并成为会话组的首领进程(leader=1), 首领进程的会话号(session)和进程组号(pgrp)等于进程号(pid). 首领进程打开终端文件后成为终端的控制进程, 其tty指针指向该终端打开结构, 该终端也成为本次会话的控制终端, 其session号和pgrp号分别等于进程的session号和pgrp号. 当用户从终端登录后, 登录bash程序置换getty进程成为首领进程. 进程的会话号和组号随着进程的复制而传递, 子进程的首领标志复位. bash在用execve()执行用户程序之前将用setpgid()为每一程序建立属于它自已的进程组(使进程组号等于进程号), 再用对终端的(TIOCSPGRP)设备控制将终端进程组号设为将该进程号, 使终端切换到新的进程组. 终端进程组以外的进程组如果进行读操作将在其进程组中生成SIGTTIN信号, 缺省的信号处理器使进程进入暂停. ; drivers/char/keyboard.c: void put_queue(int ch) { wake_up(&keypress_wait); if (tty) { tty_insert_flip_char(tty, ch, 0); con_schedule_flip(tty); } } static void puts_queue(char *cp) { wake_up(&keypress_wait); if (!tty) return; while (*cp) { tty_insert_flip_char(tty, *cp, 0); cp++; } con_schedule_flip(tty); } _INLINE_ void tty_insert_flip_char(struct tty_struct *tty, unsigned char ch, char flag) { if (tty->flip.count < TTY_FLIPBUF_SIZE) { tty->flip.count++; *tty->flip.flag_buf_ptr++ = flag; *tty->flip.char_buf_ptr++ = ch; } } extern inline void con_schedule_flip(struct tty_struct *t) { queue_task(&t->flip.tqueue, &con_task_queue); tasklet_schedule(&console_tasklet); } ; drivers/char/tty_io.c: /* * When a break, frame error, or parity error happens, these codes are * stuffed into the flags buffer. */ #define TTY_NORMAL 0 #define TTY_BREAK 1 #define TTY_FRAME 2 #define TTY_PARITY 3 #define TTY_OVERRUN 4 #define INTR_CHAR(tty) ((tty)->termios->c_cc[VINTR]) #define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT]) #define ERASE_CHAR(tty) ((tty)->termios->c_cc[VERASE]) #define KILL_CHAR(tty) ((tty)->termios->c_cc[VKILL]) #define EOF_CHAR(tty) ((tty)->termios->c_cc[VEOF]) #define TIME_CHAR(tty) ((tty)->termios->c_cc[VTIME]) #define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN]) #define SWTC_CHAR(tty) ((tty)->termios->c_cc[VSWTC]) #define START_CHAR(tty) ((tty)->termios->c_cc[VSTART]) #define STOP_CHAR(tty) ((tty)->termios->c_cc[VSTOP]) #define SUSP_CHAR(tty) ((tty)->termios->c_cc[VSUSP]) #define EOL_CHAR(tty) ((tty)->termios->c_cc[VEOL]) #define REPRINT_CHAR(tty) ((tty)->termios->c_cc[VREPRINT]) #define DISCARD_CHAR(tty) ((tty)->termios->c_cc[VDISCARD]) #define WERASE_CHAR(tty) ((tty)->termios->c_cc[VWERASE]) #define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT]) #define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2]) #define _I_FLAG(tty,f) ((tty)->termios->c_iflag & (f)) #define _O_FLAG(tty,f) ((tty)->termios->c_oflag & (f)) #define _C_FLAG(tty,f) ((tty)->termios->c_cflag & (f)) #define _L_FLAG(tty,f) ((tty)->termios->c_lflag & (f)) #define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK) #define I_BRKINT(tty) _I_FLAG((tty),BRKINT) #define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR) #define I_PARMRK(tty) _I_FLAG((tty),PARMRK) #define I_INPCK(tty) _I_FLAG((tty),INPCK) #define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP) #define I_INLCR(tty) _I_FLAG((tty),INLCR) #define I_IGNCR(tty) _I_FLAG((tty),IGNCR) #define I_ICRNL(tty) _I_FLAG((tty),ICRNL) #define I_IUCLC(tty) _I_FLAG((tty),IUCLC) #define I_IXON(tty) _I_FLAG((tty),IXON) #define I_IXANY(tty) _I_FLAG((tty),IXANY) #define I_IXOFF(tty) _I_FLAG((tty),IXOFF) #define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL) #define O_OPOST(tty) _O_FLAG((tty),OPOST) #define O_OLCUC(tty) _O_FLAG((tty),OLCUC) #define O_ONLCR(tty) _O_FLAG((tty),ONLCR) #define O_OCRNL(tty) _O_FLAG((tty),OCRNL) #define O_ONOCR(tty) _O_FLAG((tty),ONOCR) #define O_ONLRET(tty) _O_FLAG((tty),ONLRET) #define O_OFILL(tty) _O_FLAG((tty),OFILL) #define O_OFDEL(tty) _O_FLAG((tty),OFDEL) #define O_NLDLY(tty) _O_FLAG((tty),NLDLY) #define O_CRDLY(tty) _O_FLAG((tty),CRDLY) #define O_TABDLY(tty) _O_FLAG((tty),TABDLY) #define O_BSDLY(tty) _O_FLAG((tty),BSDLY) #define O_VTDLY(tty) _O_FLAG((tty),VTDLY) #define O_FFDLY(tty) _O_FLAG((tty),FFDLY) #define C_BAUD(tty) _C_FLAG((tty),CBAUD) #define C_CSIZE(tty) _C_FLAG((tty),CSIZE) #define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB) #define C_CREAD(tty) _C_FLAG((tty),CREAD) #define C_PARENB(tty) _C_FLAG((tty),PARENB) #define C_PARODD(tty) _C_FLAG((tty),PARODD) #define C_HUPCL(tty) _C_FLAG((tty),HUPCL) #define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL) #define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD) #define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS) #define L_ISIG(tty) _L_FLAG((tty),ISIG) #define L_ICANON(tty) _L_FLAG((tty),ICANON) #define L_XCASE(tty) _L_FLAG((tty),XCASE) #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_ECHONL(tty) _L_FLAG((tty),ECHONL) #define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH) #define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) #define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) #define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT) #define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) #define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO) #define L_PENDIN(tty) _L_FLAG((tty),PENDIN) #define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN) /* number of characters left in xmit buffer before select has we have room */ #define WAKEUP_CHARS 256 typedef unsigned char cc_t; typedef unsigned int speed_t; typedef unsigned int tcflag_t; #define NCCS 19 struct termios { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[NCCS]; /* control characters */ }; /* intr=^C quit=^ erase=del kill=^U eof=^D vtime= vmin=1 sxtc= start=^Q stop=^S susp=^Z eol= reprint=^R discard=^U werase=^W lnext=^V eol2= */ #define INIT_C_CC "