📄 tty_io.c
字号:
// 将tty 辅助队列头指针后退1 字节,继续处理其它字符。DEC(tty->secondary.head);continue;}//如果该字符是停止字符(^S),则置tty 停止标志,继续处理其它字符。if (c==STOP_CHAR(tty)) {tty->stopped=1;continue;}// 如果该字符是停止字符(^Q),则复位tty 停止标志,继续处理其它字符。if (c==START_CHAR(tty)) {tty->stopped=0;continue;}}// 若输入模式标志集中ISIG 标志置位,则在收到INTR、QUIT、SUSP 或DSUSP 字符时,需要为进程// 产生相应的信号。if (L_ISIG(tty)) {// 如果该字符是键盘中断符(^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);}} elsePUTCH(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;elsecurrent->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(tty_table+tty);}//// 字符设备初始化函数。空,为以后扩展做准备。void chr_dev_init(void){}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -