📄 tty_io.c
字号:
if (c == INTR_CHAR (tty)) { tty_intr (tty, INTMASK); continue; } // 如果该字符是键盘中断符(^\),则向当前进程发送键盘退出信号,并继续处理下一字符。 if (c == QUIT_CHAR (tty)) { tty_intr (tty, QUITMASK); continue; } } // 如果该字符是换行符NL(10),或者是文件结束符EOF(^D),辅助缓冲队列字符数加1。[??] if (c == 10 || c == EOF_CHAR (tty)) tty->secondary.data++; // 如果本地模式标志集中回显标志ECHO 置位,那么,如果字符是换行符NL(10),则将换行符NL(10) // 和回车符CR(13)放入tty 写队列缓冲区中;如果字符是控制字符(字符值<32)并且回显控制字符标志 // ECHOCTL 置位,则将字符'^'和字符c+64 放入tty 写队列中(也即会显示^C、^H 等);否则将该字符 // 直接放入tty 写缓冲队列中。最后调用该tty 的写操作函数。 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);}//// tty 读函数。// 参数:channel - 子设备号;buf - 缓冲区指针;nr - 欲读字节数。// 返回已读字节数。int tty_read (unsigned channel, char *buf, int nr){ struct tty_struct *tty; char c, *b = buf; int minimum, time, flag = 0; long oldalarm; // 本版本linux 内核的终端只有3 个子设备,分别是控制台(0)、串口终端1(1)和串口终端2(2)。 // 所以任何大于2 的子设备号都是非法的。写的字节数当然也不能小于0 的。 if (channel > 2 || nr < 0) return -1; // tty 指针指向子设备号对应ttb_table 表中的tty 结构。 tty = &tty_table[channel]; // 下面首先保存进程原定时值,然后根据控制字符VTIME 和VMIN 设置读字符操作的超时定时值。 // 在非规范模式下,这两个值是超时定时值。MIN 表示为了满足读操作,需要读取的最少字符数。 // TIME 是一个十分之一秒计数的计时值。 // 首先取进程中的(报警)定时值(滴答数)。 oldalarm = current->alarm; // 并设置读操作超时定时值time 和需要最少读取的字符个数minimum。 time = 10L * tty->termios.c_cc[VTIME]; minimum = tty->termios.c_cc[VMIN]; // 如果设置了读超时定时值time 但没有设置最少读取个数minimum,那么在读到至少一个字符或者 // 定时超时后读操作将立刻返回。所以这里置minimum=1。 if (time && !minimum) { minimum = 1; // 如果进程原定时值是0 或者time+当前系统时间值小于进程原定时值的话,则置重新设置进程定时 // 值为time+当前系统时间,并置flag 标志。 if (flag = (!oldalarm || time + jiffies < oldalarm)) current->alarm = time + jiffies; } // 如果设置的最少读取字符数>欲读的字符数,则令其等于此次欲读取的字符数。 if (minimum > nr) minimum = nr; // 当欲读的字节数>0,则循环执行以下操作。 while (nr > 0) { // 如果flag 不为0(即进程原定时值是0 或者time+当前系统时间值小于进程原定时值)并且进程有定 // 时信号SIGALRM,则复位进程的定时信号并中断循环。 if (flag && (current->signal & ALRMMASK)) { current->signal &= ~ALRMMASK; break; } // 如果当前进程有信号要处理,则退出,返回0。 if (current->signal) break; // 如果辅助缓冲队列(规范模式队列)为空,或者设置了规范模式标志并且辅助队列中字符数为0 以及 // 辅助模式缓冲队列空闲空间>20,则进入可中断睡眠状态,返回后继续处理。 if (EMPTY (tty->secondary) || (L_CANON (tty) && !tty->secondary.data && LEFT (tty->secondary) > 20)) { sleep_if_empty (&tty->secondary); continue; } // 执行以下操作,直到nr=0 或者辅助缓冲队列为空。 do { // 取辅助缓冲队列字符c。 GETCH (tty->secondary, c); // 如果该字符是文件结束符(^D)或者是换行符NL(10),则辅助缓冲队列字符数减1。 if (c == EOF_CHAR (tty) || c == 10) tty->secondary.data--; // 如果该字符是文件结束符(^D)并且规范模式标志置位,则返回已读字符数,并退出。 if (c == EOF_CHAR (tty) && L_CANON (tty)) return (b - buf); // 否则将该字符放入用户数据段缓冲区buf 中,欲读字符数减1,如果欲读字符数已为0,则中断循环。 else { put_fs_byte (c, b++); if (!--nr) break; } } while (nr > 0 && !EMPTY (tty->secondary)); // 如果超时定时值time 不为0 并且规范模式标志没有置位(非规范模式),那么: if (time && !L_CANON (tty)) // 如果进程原定时值是0 或者time+当前系统时间值小于进程原定时值的话,则置重新设置进程定时值 // 为time+当前系统时间,并置flag 标志。否则让进程的定时值等于进程原定时值。 if (flag = (!oldalarm || time + jiffies < oldalarm)) current->alarm = time + jiffies; else current->alarm = oldalarm; // 如果规范模式标志置位,那么若没有读到1 个字符则中断循环。否则若已读取数大于或等于最少要 // 求读取的字符数,则也中断循环。 if (L_CANON (tty)) { if (b - buf) break; } else if (b - buf >= minimum) break; } // 让进程的定时值等于进程原定时值。 current->alarm = oldalarm; // 如果进程有信号并且没有读取任何字符,则返回出错号(超时)。 if (current->signal && !(b - buf)) return -EINTR; return (b - buf); /* 返回已读取的字符数*/}//// tty 写函数。// 参数:channel - 子设备号;buf - 缓冲区指针;nr - 写字节数。// 返回已写字节数。int tty_write (unsigned channel, char *buf, int nr){ static cr_flag = 0; struct tty_struct *tty; char c, *b = buf; // 本版本linux 内核的终端只有3 个子设备,分别是控制台(0)、串口终端1(1)和串口终端2(2)。 // 所以任何大于2 的子设备号都是非法的。写的字节数当然也不能小于0 的。 if (channel > 2 || nr < 0) return -1; // tty 指针指向子设备号对应ttb_table 表中的tty 结构。 tty = channel + tty_table; // 字符设备是一个一个字符进行处理的,所以这里对于nr 大于0 时对每个字符进行循环处理。 while (nr > 0) { // 如果此时tty 的写队列已满,则当前进程进入可中断的睡眠状态。 sleep_if_full (&tty->write_q); // 如果当前进程有信号要处理,则退出,返回0。 if (current->signal) break; // 当要写的字节数>0 并且tty 的写队列不满时,循环执行以下操作。 while (nr > 0 && !FULL (tty->write_q)) { // 从用户数据段内存中取一字节c。 c = get_fs_byte (b); // 如果终端输出模式标志集中的执行输出处理标志OPOST 置位,则执行下列输出时处理过程。 if (O_POST (tty)) { // 如果该字符是回车符'\r'(CR,13)并且回车符转换行符标志OCRNL 置位,则将该字符换成换行符 // '\n'(NL,10);否则如果该字符是换行符'\n'(NL,10)并且换行转回车功能标志ONLRET 置位的话, // 则将该字符换成回车符'\r'(CR,13)。 if (c == '\r' && O_CRNL (tty)) c = '\n'; else if (c == '\n' && O_NLRET (tty)) c = '\r'; // 如果该字符是换行符'\n'并且回车标志cr_flag 没有置位,换行转回车-换行标志ONLCR 置位的话, // 则将cr_flag 置位,并将一回车符放入写队列中。然后继续处理下一个字符。 if (c == '\n' && !cr_flag && O_NLCR (tty)) { cr_flag = 1; PUTCH (13, tty->write_q); continue; } // 如果小写转大写标志OLCUC 置位的话,就将该字符转成大写字符。 if (O_LCUC (tty)) c = toupper (c); } // 用户数据缓冲指针b 前进1 字节;欲写字节数减1 字节;复位cr_flag 标志,并将该字节放入tty // 写队列中。 b++; nr--; cr_flag = 0; PUTCH (c, tty->write_q); } // 若字节全部写完,或者写队列已满,则程序执行到这里。调用对应tty 的写函数,若还有字节要写, // 则等待写队列不满,所以调用调度程序,先去执行其它任务。 tty->write (tty); if (nr > 0) schedule (); } return (b - buf); /*返回写入的字节数*/}/** Jeh, sometimes I really like the 386.* This routine is called from an interrupt,* and there should be absolutely no problem* with sleeping even in an interrupt (I hope).* Of course, if somebody proves me wrong, I'll* hate intel for all time :-). We'll have to* be careful and see to reinstating the interrupt* chips before calling this, though.** I don't think we sleep here under normal circumstances* anyway, which is good, as the task sleeping might be* totally innocent.*//** 呵,有时我是真得很喜欢386。该子程序是从一个中断处理程序中调用的,即使在* 中断处理程序中睡眠也应该绝对没有问题(我希望如此)。当然,如果有人证明我是* 错的,那么我将憎恨intel 一辈子?。但是我们必须小心,在调用该子程序之前需* 要恢复中断。** 我不认为在通常环境下会处在这里睡眠,这样很好,因为任务睡眠是完全任意的。*/// tty 中断处理调用函数 - 执行tty 中断处理。// 参数:tty - 指定的tty 终端号(0,1 或2)。// 将指定tty 终端队列缓冲区中的字符复制成规范(熟)模式字符并存放在辅助队列(规范模式队列)中。// 在串口读字符中断(rs_io.s, 109)和键盘中断(kerboard.S, 69)中调用。void do_tty_interrupt (int tty){ copy_to_cooked(struct tty_struct * tty) (tty_table + tty);}// 字符设备初始化函数。空,为以后扩展做准备。void chr_dev_init (void){}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -