TTY终端的输入过程

来源:互联网 发布:淘宝智能版有间隙 编辑:程序博客网 时间:2024/04/29 00:20
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_NORMAL0#define TTY_BREAK1#define TTY_FRAME2#define TTY_PARITY3#define TTY_OVERRUN4#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 256typedef unsigned charcc_t;typedef unsigned intspeed_t;typedef unsigned inttcflag_t;#define NCCS 19struct 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=^Cquit=^\erase=delkill=^Ueof=^Dvtime=\0vmin=\1sxtc=\0start=^Qstop=^Ssusp=^Zeol=\0reprint=^Rdiscard=^Uwerase=^Wlnext=^Veol2=\0*/#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"/* c_cc characters */#define VINTR 0#define VQUIT 1#define VERASE 2#define VKILL 3#define VEOF 4#define VTIME 5#define VMIN 6#define VSWTC 7#define VSTART 8#define VSTOP 9#define VSUSP 10#define VEOL 11#define VREPRINT 12#define VDISCARD 13#define VWERASE 14#define VLNEXT 15#define VEOL2 16/* c_iflag bits */#define IGNBRK0000001#define BRKINT0000002#define IGNPAR0000004#define PARMRK0000010#define INPCK0000020#define ISTRIP0000040#define INLCR0000100#define IGNCR0000200#define ICRNL0000400#define IUCLC0001000#define IXON0002000#define IXANY0004000#define IXOFF0010000#define IMAXBEL0020000/* c_oflag bits */#define OPOST0000001#define OLCUC0000002#define ONLCR0000004#define OCRNL0000010#define ONOCR0000020#define ONLRET0000040#define OFILL0000100#define OFDEL0000200#define NLDLY0000400#define   NL00000000#define   NL10000400#define CRDLY0003000#define   CR00000000#define   CR10001000#define   CR20002000#define   CR30003000#define TABDLY0014000#define   TAB00000000#define   TAB10004000#define   TAB20010000#define   TAB30014000#define   XTABS0014000#define BSDLY0020000#define   BS00000000#define   BS10020000#define VTDLY0040000#define   VT00000000#define   VT10040000#define FFDLY0100000#define   FF00000000#define   FF10100000/* c_cflag bit meaning */#define CBAUD0010017#define  B00000000/* hang up */#define  B500000001#define  B750000002#define  B1100000003#define  B1340000004#define  B1500000005#define  B2000000006#define  B3000000007#define  B6000000010#define  B12000000011#define  B18000000012#define  B24000000013#define  B48000000014#define  B96000000015#define  B192000000016#define  B384000000017#define EXTA B19200#define EXTB B38400#define CSIZE0000060#define   CS50000000#define   CS60000020#define   CS70000040#define   CS80000060#define CSTOPB0000100#define CREAD0000200#define PARENB0000400#define PARODD0001000#define HUPCL0002000#define CLOCAL0004000#define CBAUDEX 0010000#define    B57600 0010001#define   B115200 0010002#define   B230400 0010003#define   B460800 0010004#define   B500000 0010005#define   B576000 0010006#define   B921600 0010007#define  B1000000 0010010#define  B1152000 0010011#define  B1500000 0010012#define  B2000000 0010013#define  B2500000 0010014#define  B3000000 0010015#define  B3500000 0010016#define  B4000000 0010017#define CIBAUD  002003600000/* input baud rate (not used) */#define CMSPAR  010000000000/* mark or space (stick) parity */#define CRTSCTS  020000000000/* flow control *//* c_lflag bits */#define ISIG0000001#define ICANON0000002#define XCASE0000004#define ECHO0000010#define ECHOE0000020#define ECHOK0000040#define ECHONL0000100#define NOFLSH0000200#define TOSTOP0000400#define ECHOCTL0001000#define ECHOPRT0002000#define ECHOKE0004000#define FLUSHO0010000#define PENDIN0040000#define IEXTEN0100000/* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver.  These watermarks are used for * controlling the space in the read buffer. */#define TTY_THRESHOLD_THROTTLE128 /* now based on remaining room */#define TTY_THRESHOLD_UNTHROTTLE 128static struct file_operations tty_fops = {llseek:tty_lseek,read:tty_read,write:tty_write,poll:tty_poll,ioctl:tty_ioctl,open:tty_open,release:tty_release,fasync:tty_fasync,};void __init console_init(void){/* Setup the default TTY line discipline. */memset(ldiscs, 0, sizeof(ldiscs));(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);/* * Set up the standard termios.  Individual tty drivers may  * deviate from this; this is used as a template. */memset(&tty_std_termios, 0, sizeof(struct termios));memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);tty_std_termios.c_iflag = ICRNL | IXON;tty_std_termios.c_oflag = OPOST | ONLCR;tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |ECHOCTL | ECHOKE | IEXTEN;...}static ssize_t tty_read(struct file * file, char * buf, size_t count, loff_t *ppos){int i;struct tty_struct * tty;struct inode *inode;/* Can't seek (pread) on ttys.  */if (ppos != &file->f_pos)return -ESPIPE;tty = (struct tty_struct *)file->private_data;inode = file->f_dentry->d_inode;if (tty_paranoia_check(tty, inode->i_rdev, "tty_read")) 校验tty结构是否完整return -EIO;if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))return -EIO;/* This check not only needs to be done before reading, but also   whenever read_chan() gets woken up after sleeping, so I've   moved it to there.  This should only be done for the N_TTY   line discipline, anyway.  Same goes for write_chan(). -- jlc. */#if 0if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */    (tty->pgrp > 0) &&    (current->tty == tty) &&    (tty->pgrp != current->pgrp))if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))return -EIO;else {(void) kill_pg(current->pgrp, SIGTTIN, 1);return -ERESTARTSYS;}#endiflock_kernel();if (tty->ldisc.read)i = (tty->ldisc.read)(tty,file,buf,count);elsei = -EIO;unlock_kernel();if (i > 0)inode->i_atime = CURRENT_TIME;return i;}/* * This routine is called out of the software interrupt to flush data * from the flip buffer to the line discipline. */static void flush_to_ldisc(void *private_){struct tty_struct *tty = (struct tty_struct *) private_;unsigned char*cp;char*fp;intcount;unsigned long flags;if (test_bit(TTY_DONT_FLIP, &tty->flags)) { 如果不允许翻转读写缓冲区queue_task(&tty->flip.tqueue, &tq_timer); 将任务延迟一个时钟中断return;}if (tty->flip.buf_num) {cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;tty->flip.buf_num = 0;save_flags(flags); cli();tty->flip.char_buf_ptr = tty->flip.char_buf;tty->flip.flag_buf_ptr = tty->flip.flag_buf;} else {cp = tty->flip.char_buf;fp = tty->flip.flag_buf;tty->flip.buf_num = 1;save_flags(flags); cli();tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;}count = tty->flip.count;tty->flip.count = 0;restore_flags(flags);tty->ldisc.receive_buf(tty, cp, fp, count);}void start_tty(struct tty_struct *tty){if (!tty->stopped || tty->flow_stopped)return;tty->stopped = 0;if (tty->link && tty->link->packet) {tty->ctrl_status &= ~TIOCPKT_STOP;tty->ctrl_status |= TIOCPKT_START;wake_up_interruptible(&tty->link->read_wait);}if (tty->driver.start)(tty->driver.start)(tty);if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&    tty->ldisc.write_wakeup)(tty->ldisc.write_wakeup)(tty);wake_up_interruptible(&tty->write_wait);}void stop_tty(struct tty_struct *tty){if (tty->stopped)return;tty->stopped = 1;if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; wake_up_interruptible(&tty->link->read_wait);}if (tty->driver.stop)(tty->driver.stop)(tty);}; drivers/char/n_tty.c:struct tty_ldisc tty_ldisc_N_TTY = {TTY_LDISC_MAGIC,/* magic */"n_tty",/* name */0,/* num */0,/* flags */n_tty_open,/* open */n_tty_close,/* close */n_tty_flush_buffer,/* flush_buffer */n_tty_chars_in_buffer,/* chars_in_buffer */read_chan,/* read */write_chan,/* write */n_tty_ioctl,/* ioctl */n_tty_set_termios,/* set_termios */normal_poll,/* poll */n_tty_receive_buf,/* receive_buf */n_tty_receive_room,/* receive_room */0/* write_wakeup */};static ssize_t read_chan(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr){unsigned char *b = buf;DECLARE_WAITQUEUE(wait, current);int c;int minimum, time;ssize_t retval = 0;ssize_t size;long timeout;unsigned long flags;do_it_again:if (!tty->read_buf) {printk("n_tty_read_chan: called with read_buf == NULL?!?\n");return -EIO;}/* Job control check -- must be done at start and after   every sleep (POSIX.1 7.1.1.4). *//* NOTE: not yet done after every sleep pending a thorough   check of the logic of this change. -- jlc *//* don't stop on /dev/console */if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && 打开的不是/dev/tty设备    file->f_dentry->d_inode->i_rdev != SYSCONS_DEV && 打开的不是/dev/console设备    current->tty == tty) { if (tty->pgrp <= 0)printk("read_chan: tty->pgrp <= 0!\n");else if (current->pgrp != tty->pgrp) { 如果当前进程不属于终端的当前进程组if (is_ignored(SIGTTIN) || 如果SIGTTIN信号被忽略    is_orphaned_pgrp(current->pgrp)) 如果当前进程组的父进程已退出return -EIO;kill_pg(current->pgrp, SIGTTIN, 1); return -ERESTARTSYS;}}minimum = time = 0;timeout = MAX_SCHEDULE_TIMEOUT;if (!tty->icanon) { 如果不是规范输入模式time = (HZ / 10) * TIME_CHAR(tty); 计算超时时间, 时间字节的单位为0.1秒minimum = MIN_CHAR(tty); 返回的最少字符数if (minimum) { if (time) tty->minimum_to_wake = 1; 接收到1个字符后就唤醒else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum))tty->minimum_to_wake = minimum;} else { 如果最少返回字符为零timeout = 0; if (time) {timeout = time;time = 0;}tty->minimum_to_wake = minimum = 1;}}if (file->f_flags & O_NONBLOCK) {if (down_trylock(&tty->atomic_read))return -EAGAIN;}else {if (down_interruptible(&tty->atomic_read))return -ERESTARTSYS;}add_wait_queue(&tty->read_wait, &wait);set_bit(TTY_DONT_FLIP, &tty->flags); 禁止缓冲区翻转while (nr) {/* First test for status change. */if (tty->packet && tty->link->ctrl_status) {unsigned char cs;if (b != buf)break;cs = tty->link->ctrl_status;tty->link->ctrl_status = 0;put_user(cs, b++); nr--;break; }/* This statement must be first before checking for input   so that any interrupt will set the state back to   TASK_RUNNING. */set_current_state(TASK_INTERRUPTIBLE);if (((minimum - (b - buf)) < tty->minimum_to_wake) &&    ((minimum - (b - buf)) >= 1))tty->minimum_to_wake = (minimum - (b - buf));if (!input_available_p(tty, 0)) {if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {retval = -EIO;break;}if (tty_hung_up_p(file))break;if (!timeout)break;if (file->f_flags & O_NONBLOCK) {retval = -EAGAIN;break;}if (signal_pending(current)) {retval = -ERESTARTSYS;break;}clear_bit(TTY_DONT_FLIP, &tty->flags);timeout = schedule_timeout(timeout);set_bit(TTY_DONT_FLIP, &tty->flags);continue;}current->state = TASK_RUNNING;/* Deal with packet mode. */if (tty->packet && b == buf) {put_user(TIOCPKT_DATA, b++);nr--;}if (tty->icanon) {/* N.B. avoid overrun if nr == 0 */while (nr && tty->read_cnt) { int eol;eol = test_and_clear_bit(tty->read_tail,&tty->read_flags);c = tty->read_buf[tty->read_tail];spin_lock_irqsave(&tty->read_lock, flags);tty->read_tail = ((tty->read_tail+1) &  (N_TTY_BUF_SIZE-1));tty->read_cnt--;if (eol) {/* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */if (--tty->canon_data < 0)tty->canon_data = 0;}spin_unlock_irqrestore(&tty->read_lock, flags);if (!eol || (c != __DISABLED_CHAR)) {put_user(c, b++);nr--;}if (eol)break;}} else {int uncopied;uncopied = copy_from_read_buf(tty, &b, &nr);uncopied += copy_from_read_buf(tty, &b, &nr);if (uncopied) {retval = -EFAULT;break;}}/* If there is enough space in the read buffer now, let the * low-level driver know. We use n_tty_chars_in_buffer() to * check the buffer, as it now knows about canonical mode. * Otherwise, if the driver is throttled and the line is * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE)check_unthrottle(tty); 如果读缓冲区的字符小于128, 打开节流的输入设备if (b - buf >= minimum)break;if (time)timeout = time;}clear_bit(TTY_DONT_FLIP, &tty->flags);up(&tty->atomic_read);remove_wait_queue(&tty->read_wait, &wait);if (!waitqueue_active(&tty->read_wait))tty->minimum_to_wake = minimum;current->state = TASK_RUNNING;size = b - buf;if (size) {retval = size;if (nr)       clear_bit(TTY_PUSH, &tty->flags);} else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again;return retval;}int is_ignored(int sig){return (sigismember(¤t->blocked, sig) ||        current->sig->action[sig-1].sa.sa_handler == SIG_IGN);}static inline int input_available_p(struct tty_struct *tty, int amt){if (tty->icanon) {if (tty->canon_data)return 1;} else if (tty->read_cnt >= (amt ? amt : 1))return 1;return 0;}int tty_hung_up_p(struct file * filp){return (filp->f_op == &hung_up_tty_fops);}/* * Helper function to speed up read_chan.  It is only called when * ICANON is off; it copies characters straight from the tty queue to * user space directly.  It can be profitably called twice; once to * drain the space from the tail pointer to the (physical) end of the * buffer, and once to drain the space from the (physical) beginning of * the buffer to head pointer. */static inline int copy_from_read_buf(struct tty_struct *tty,      unsigned char **b,      size_t *nr){int retval;ssize_t n;unsigned long flags;retval = 0;spin_lock_irqsave(&tty->read_lock, flags);n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));spin_unlock_irqrestore(&tty->read_lock, flags);if (n) {mb();retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);n -= retval;spin_lock_irqsave(&tty->read_lock, flags);tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);tty->read_cnt -= n;spin_unlock_irqrestore(&tty->read_lock, flags);*b += n;*nr -= n;}return retval;}/* * Return number of characters buffered to be delivered to user */ssize_t n_tty_chars_in_buffer(struct tty_struct *tty){unsigned long flags;ssize_t n = 0;spin_lock_irqsave(&tty->read_lock, flags);if (!tty->icanon) {n = tty->read_cnt;} else if (tty->canon_data) {n = (tty->canon_head > tty->read_tail) ?tty->canon_head - tty->read_tail :tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);}spin_unlock_irqrestore(&tty->read_lock, flags);return n;}/*  * Check whether to call the driver.unthrottle function. * We test the TTY_THROTTLED bit first so that it always * indicates the current state. */static void check_unthrottle(struct tty_struct * tty){if (tty->count &&    test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&     tty->driver.unthrottle)tty->driver.unthrottle(tty);}static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,      char *fp, int count){const unsigned char *p;char *f, flags = TTY_NORMAL;inti;charbuf[64];unsigned long cpuflags;if (!tty->read_buf)return;if (tty->real_raw) { 如果终端处于原始接收模式, 直接输出翻转缓冲区字符spin_lock_irqsave(&tty->read_lock, cpuflags);i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,   N_TTY_BUF_SIZE - tty->read_head));memcpy(tty->read_buf + tty->read_head, cp, i);tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);tty->read_cnt += i;cp += i;count -= i;i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,       N_TTY_BUF_SIZE - tty->read_head));memcpy(tty->read_buf + tty->read_head, cp, i);tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);tty->read_cnt += i;spin_unlock_irqrestore(&tty->read_lock, cpuflags);} else {for (i=count, p = cp, f = fp; i; i--, p++) {if (f)flags = *f++;switch (flags) {case TTY_NORMAL:n_tty_receive_char(tty, *p);break;case TTY_BREAK:n_tty_receive_break(tty);break;case TTY_PARITY:case TTY_FRAME:n_tty_receive_parity_error(tty, *p);break;case TTY_OVERRUN:n_tty_receive_overrun(tty);break;default:printk("%s: unknown flag %d\n",       tty_name(tty, buf), flags);break;}}if (tty->driver.flush_chars)tty->driver.flush_chars(tty);}if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {kill_fasync(&tty->fasync, SIGIO, POLL_IN); 异步信号读写通知if (waitqueue_active(&tty->read_wait))wake_up_interruptible(&tty->read_wait);}/* * Check the remaining room for the input canonicalization * mode.  We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */if (n_tty_receive_room(tty) < TTY_THRESHOLD_THROTTLE) { 如果剩余空间小于128字节/* check TTY_THROTTLED first so it indicates our state */if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&    tty->driver.throttle)tty->driver.throttle(tty); 进行节流处理}}static inline void n_tty_receive_break(struct tty_struct *tty){if (I_IGNBRK(tty)) 如果忽略中断字符return;if (I_BRKINT(tty)) { 如果中断字符产生中信号isig(SIGINT, tty, 1); 给终端所在的进程组发送INT信号return;}if (I_PARMRK(tty)) { 断点标记put_tty_queue('\377', tty);put_tty_queue('\0', tty);}put_tty_queue('\0', tty);wake_up_interruptible(&tty->read_wait);}static inline void n_tty_receive_overrun(struct tty_struct *tty){char buf[64];tty->num_overrun++;if (time_before(tty->overrun_time, jiffies - HZ)) {printk("%s: %d input overrun(s)\n", tty_name(tty, buf),       tty->num_overrun);tty->overrun_time = jiffies;tty->num_overrun = 0;}}static inline void n_tty_receive_parity_error(struct tty_struct *tty,      unsigned char c){if (I_IGNPAR(tty)) {return;}if (I_PARMRK(tty)) {put_tty_queue('\377', tty);put_tty_queue('\0', tty);put_tty_queue(c, tty);} elseif (I_INPCK(tty)) 允许奇偶校验put_tty_queue('\0', tty);elseput_tty_queue(c, tty);wake_up_interruptible(&tty->read_wait);}static int n_tty_receive_room(struct tty_struct *tty){intleft = N_TTY_BUF_SIZE - tty->read_cnt - 1;/* * If we are doing input canonicalization, and there are no * pending newlines, let characters through without limit, so * that erase characters will be handled.  Other excess * characters will be beeped. */if (tty->icanon && !tty->canon_data)return N_TTY_BUF_SIZE;if (left > 0)return left;return 0;}static inline void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty){if (tty->read_cnt < N_TTY_BUF_SIZE) {tty->read_buf[tty->read_head] = c;tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);tty->read_cnt++;}}static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c){unsigned long flags;if (tty->raw) {put_tty_queue(c, tty);return;}if (tty->stopped && !tty->flow_stopped &&    I_IXON(tty) && I_IXANY(tty)) { 如果软件流控制start_tty(tty);return;}if (I_ISTRIP(tty)) 如果去除字符最高位c &= 0x7f;if (I_IUCLC(tty) && L_IEXTEN(tty)) 如果允许小写转换扩展模式c=tolower(c);if (tty->closing) { 如果正在关闭终端if (I_IXON(tty)) {if (c == START_CHAR(tty))start_tty(tty);else if (c == STOP_CHAR(tty))stop_tty(tty);}return;}/* * If the previous character was LNEXT, or we know that this * character is not one of the characters that we'll have to * handle specially, do shortcut processing to speed things * up. */if (!test_bit(c, &tty->process_char_map) || tty->lnext) {finish_erasing(tty);tty->lnext = 0;if (L_ECHO(tty)) { 如果输入字符回显if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { 如果输入缓冲区满put_char('\a', tty); /* beep if no space */ 向显示终端发送响铃字符return;}/* Record the column of first canon char. */if (tty->canon_head == tty->read_head)tty->canon_column = tty->column;echo_char(c, tty);}if (I_PARMRK(tty) && c == (unsigned char) '\377')put_tty_queue(c, tty);put_tty_queue(c, tty);return;}if (c == '\r') {if (I_IGNCR(tty)) 忽略回车符return;if (I_ICRNL(tty)) 回车转换为换行符c = '\n';} else if (c == '\n' && I_INLCR(tty)) 换行符转换成回车符c = '\r';if (I_IXON(tty)) { 如果为软件流控if (c == START_CHAR(tty)) { 如果为启动字符start_tty(tty); 打开终端return;}if (c == STOP_CHAR(tty)) { 如果为停止字符stop_tty(tty); 暂停终端return;}}if (L_ISIG(tty)) { 如果允许触发信号int signal;signal = SIGINT;if (c == INTR_CHAR(tty)) 如果为中断字符goto send_signal;signal = SIGQUIT;if (c == QUIT_CHAR(tty)) 如果为退出字符goto send_signal;signal = SIGTSTP;if (c == SUSP_CHAR(tty)) { 如果为暂停字符send_signal:isig(signal, tty, 0); 向终端所在的进程组发送相应的信号return;}}if (tty->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty);return;}if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {tty->lnext = 1;if (L_ECHO(tty)) {finish_erasing(tty);if (L_ECHOCTL(tty)) {put_char('^', tty);put_char('\b', tty);}}return;}if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&    L_IEXTEN(tty)) {unsigned long tail = tty->canon_head;finish_erasing(tty);echo_char(c, tty);opost('\n', tty);while (tail != tty->read_head) {echo_char(tty->read_buf[tail], tty);tail = (tail+1) & (N_TTY_BUF_SIZE-1);}return;}if (c == '\n') {if (L_ECHO(tty) || L_ECHONL(tty)) {if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {put_char('\a', tty);return;}opost('\n', tty);}goto handle_newline;}if (c == EOF_CHAR(tty)) {        if (tty->canon_head != tty->read_head)        set_bit(TTY_PUSH, &tty->flags);c = __DISABLED_CHAR;goto handle_newline;}if ((c == EOL_CHAR(tty)) ||    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {/* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */if (L_ECHO(tty)) {if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {put_char('\a', tty);return;}/* Record the column of first canon char. */if (tty->canon_head == tty->read_head)tty->canon_column = tty->column;echo_char(c, tty);}/* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */if (I_PARMRK(tty) && c == (unsigned char) '\377')put_tty_queue(c, tty);handle_newline:spin_lock_irqsave(&tty->read_lock, flags);set_bit(tty->read_head, &tty->read_flags);put_tty_queue_nolock(c, tty);tty->canon_head = tty->read_head;tty->canon_data++;spin_unlock_irqrestore(&tty->read_lock, flags);kill_fasync(&tty->fasync, SIGIO, POLL_IN);if (waitqueue_active(&tty->read_wait))wake_up_interruptible(&tty->read_wait);return;}}finish_erasing(tty);if (L_ECHO(tty)) {if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {put_char('\a', tty); /* beep if no space */return;}if (c == '\n')opost('\n', tty);else {/* Record the column of first canon char. */if (tty->canon_head == tty->read_head)tty->canon_column = tty->column;echo_char(c, tty);}}if (I_PARMRK(tty) && c == (unsigned char) '\377')put_tty_queue(c, tty);put_tty_queue(c, tty);}static inline void put_char(unsigned char c, struct tty_struct *tty){tty->driver.put_char(tty, c);}/* Must be called only when L_ECHO(tty) is true. */static void echo_char(unsigned char c, struct tty_struct *tty){if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {put_char('^', tty); 回显控制字符put_char(c ^ 0100, tty);tty->column += 2;} elseopost(c, tty); 回显字符}static inline void finish_erasing(struct tty_struct *tty){if (tty->erasing) {put_char('/', tty);tty->column += 2;tty->erasing = 0;}}static inline void isig(int sig, struct tty_struct *tty, int flush){if (tty->pgrp > 0)kill_pg(tty->pgrp, sig, 1);if (flush || !L_NOFLSH(tty)) {n_tty_flush_buffer(tty);if (tty->driver.flush_buffer)tty->driver.flush_buffer(tty);}}static void eraser(unsigned char c, struct tty_struct *tty){enum { ERASE, WERASE, KILL } kill_type;int head, seen_alnums;unsigned long flags;if (tty->read_head == tty->canon_head) { 如果输入为空/* opost('\a', tty); *//* what do you think? */return;}if (c == ERASE_CHAR(tty))kill_type = ERASE;else if (c == WERASE_CHAR(tty))kill_type = WERASE;else { if (!L_ECHO(tty)) { 如果禁止回显spin_lock_irqsave(&tty->read_lock, flags);tty->read_cnt -= ((tty->read_head - tty->canon_head) &  (N_TTY_BUF_SIZE - 1));tty->read_head = tty->canon_head;spin_unlock_irqrestore(&tty->read_lock, flags);return;}if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {spin_lock_irqsave(&tty->read_lock, flags);tty->read_cnt -= ((tty->read_head - tty->canon_head) &  (N_TTY_BUF_SIZE - 1));tty->read_head = tty->canon_head;spin_unlock_irqrestore(&tty->read_lock, flags);finish_erasing(tty);echo_char(KILL_CHAR(tty), tty);/* Add a newline if ECHOK is on and ECHOKE is off. */if (L_ECHOK(tty))opost('\n', tty);return;}kill_type = KILL;}seen_alnums = 0;while (tty->read_head != tty->canon_head) {head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1); c = tty->read_buf[head];if (kill_type == WERASE) {/* Equivalent to BSD's ALTWERASE. */if (isalnum(c) || c == '_')seen_alnums++;else if (seen_alnums)break;}spin_lock_irqsave(&tty->read_lock, flags);tty->read_head = head;tty->read_cnt--;spin_unlock_irqrestore(&tty->read_lock, flags);if (L_ECHO(tty)) {if (L_ECHOPRT(tty)) {if (!tty->erasing) {put_char('\\', tty);tty->column++;tty->erasing = 1;}echo_char(c, tty);} else if (kill_type == ERASE && !L_ECHOE(tty)) {echo_char(ERASE_CHAR(tty), tty);} else if (c == '\t') {unsigned int col = tty->canon_column;unsigned long tail = tty->canon_head;/* Find the column of the last char. */while (tail != tty->read_head) {c = tty->read_buf[tail];if (c == '\t')col = (col | 7) + 1;else if (iscntrl(c)) {if (L_ECHOCTL(tty))col += 2;} elsecol++;tail = (tail+1) & (N_TTY_BUF_SIZE-1);}/* should never happen */if (tty->column > 0x80000000)tty->column = 0; /* Now backup to that column. */while (tty->column > col) {/* Can't use opost here. */put_char('\b', tty);if (tty->column > 0)tty->column--;}} else {if (iscntrl(c) && L_ECHOCTL(tty)) {put_char('\b', tty);put_char(' ', tty);put_char('\b', tty);if (tty->column > 0)tty->column--;}if (!iscntrl(c) || L_ECHOCTL(tty)) {put_char('\b', tty);put_char(' ', tty);put_char('\b', tty);if (tty->column > 0)tty->column--;}}}if (kill_type == ERASE)break;}if (tty->read_head == tty->canon_head)finish_erasing(tty);}/* * Perform OPOST processing.  Returns -1 when the output device is * full and the character must be retried. */static int opost(unsigned char c, struct tty_struct *tty) 输出预处理{intspace, spaces;space = tty->driver.write_room(tty); 取输出设备剩余缓冲区空间if (!space)return -1;if (O_OPOST(tty)) { 如果允许输出预处理switch (c) {case '\n':if (O_ONLRET(tty)) 换行时是否回车tty->column = 0; if (O_ONLCR(tty)) {if (space < 2)return -1;tty->driver.put_char(tty, '\r');tty->column = 0;}tty->canon_column = tty->column;break;case '\r':if (O_ONOCR(tty) && tty->column == 0) 禁止重复输出回车符return 0;if (O_OCRNL(tty)) { 回车符转换为换行符c = '\n';if (O_ONLRET(tty))tty->canon_column = tty->column = 0;break;}tty->canon_column = tty->column = 0;break;case '\t':spaces = 8 - (tty->column & 7);if (O_TABDLY(tty) == XTABS) {if (space < spaces)return -1;tty->column += spaces;tty->driver.write(tty, 0, "        ", spaces); return 0;}tty->column += spaces;break;case '\b':if (tty->column > 0)tty->column--; 退格break;default:if (O_OLCUC(tty)) 小写转换成大写c = toupper(c); if (!iscntrl(c)) 如果不是控制符tty->column++;break;}}tty->driver.put_char(tty, c);return 0;}/* * Reset the read buffer counters, clear the flags,  * and make sure the driver is unthrottled. Called * from n_tty_open() and n_tty_flush_buffer(). */static void reset_buffer_flags(struct tty_struct *tty){unsigned long flags;spin_lock_irqsave(&tty->read_lock, flags);tty->read_head = tty->read_tail = tty->read_cnt = 0;spin_unlock_irqrestore(&tty->read_lock, flags);tty->canon_head = tty->canon_data = tty->erasing = 0;memset(&tty->read_flags, 0, sizeof tty->read_flags);check_unthrottle(tty);}static int n_tty_open(struct tty_struct *tty){if (!tty)return -EINVAL;if (!tty->read_buf) {tty->read_buf = alloc_buf();if (!tty->read_buf)return -ENOMEM;}memset(tty->read_buf, 0, N_TTY_BUF_SIZE);reset_buffer_flags(tty);tty->column = 0;n_tty_set_termios(tty, 0);tty->minimum_to_wake = 1;tty->closing = 0;return 0;}/* * This character is the same as _POSIX_VDISABLE: it cannot be used as * a c_cc[] character, but indicates that a particular special character * isn't in use (eg VINTR has no character etc) */#define __DISABLED_CHAR '\0'static void n_tty_set_termios(struct tty_struct *tty, struct termios * old){if (!tty)return;tty->icanon = (L_ICANON(tty) != 0);if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {tty->raw = 1;tty->real_raw = 1;return;}if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||    I_PARMRK(tty)) {cli();memset(tty->process_char_map, 0, 256/8);if (I_IGNCR(tty) || I_ICRNL(tty))set_bit('\r', &tty->process_char_map);if (I_INLCR(tty))set_bit('\n', &tty->process_char_map);if (L_ICANON(tty)) {set_bit(ERASE_CHAR(tty), &tty->process_char_map);set_bit(KILL_CHAR(tty), &tty->process_char_map);set_bit(EOF_CHAR(tty), &tty->process_char_map);set_bit('\n', &tty->process_char_map);set_bit(EOL_CHAR(tty), &tty->process_char_map);if (L_IEXTEN(tty)) {set_bit(WERASE_CHAR(tty),&tty->process_char_map);set_bit(LNEXT_CHAR(tty),&tty->process_char_map);set_bit(EOL2_CHAR(tty),&tty->process_char_map);if (L_ECHO(tty))set_bit(REPRINT_CHAR(tty),&tty->process_char_map);}}if (I_IXON(tty)) {set_bit(START_CHAR(tty), &tty->process_char_map);set_bit(STOP_CHAR(tty), &tty->process_char_map);}if (L_ISIG(tty)) {set_bit(INTR_CHAR(tty), &tty->process_char_map);set_bit(QUIT_CHAR(tty), &tty->process_char_map);set_bit(SUSP_CHAR(tty), &tty->process_char_map);}clear_bit(__DISABLED_CHAR, &tty->process_char_map);sti();tty->raw = 0;tty->real_raw = 0;} else {tty->raw = 1;if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&    (I_IGNPAR(tty) || !I_INPCK(tty)) &&    (tty->driver.flags & TTY_DRIVER_REAL_RAW))tty->real_raw = 1;elsetty->real_raw = 0;}}
0 0