📄 tty.c
字号:
* Give user character. */ error = ureadc(c, uio); if (error) break; if (uio->uio_resid == 0) break; /* * In canonical mode check for a "break character" * marking the end of a "line of input". */ if (ISSET(lflag, ICANON) && TTBREAKC(c)) break; first = 0; } /* * Look to unblock output now that (presumably) * the input queue has gone down. */ s = spltty(); if (ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5) { if (cc[VSTART] != _POSIX_VDISABLE && putc(cc[VSTART], &tp->t_outq) == 0) { CLR(tp->t_state, TS_TBLOCK); ttstart(tp); } } splx(s); return (error);}/* * Check the output queue on tp for space for a kernel message (from uprintf * or tprintf). Allow some space over the normal hiwater mark so we don't * lose messages due to normal flow control, but don't let the tty run amok. * Sleeps here are not interruptible, but we return prematurely if new signals * arrive. */intttycheckoutq(tp, wait) register struct tty *tp; int wait;{ int hiwat, s, oldsig; hiwat = tp->t_hiwat; s = spltty(); oldsig = wait ? curproc->p_siglist : 0; if (tp->t_outq.c_cc > hiwat + 200) while (tp->t_outq.c_cc > hiwat) { ttstart(tp); if (wait == 0 || curproc->p_siglist != oldsig) { splx(s); return (0); } timeout((void (*)__P((void *)))wakeup, (void *)&tp->t_outq, hz); SET(tp->t_state, TS_ASLEEP); sleep((caddr_t)&tp->t_outq, PZERO - 1); } splx(s); return (1);}/* * Process a write call on a tty device. */intttwrite(tp, uio, flag) register struct tty *tp; register struct uio *uio; int flag;{ register char *cp; register int cc, ce; register struct proc *p; int i, hiwat, cnt, error, s; char obuf[OBUFSIZ]; hiwat = tp->t_hiwat; cnt = uio->uio_resid; error = 0; cc = 0;loop: s = spltty(); if (!ISSET(tp->t_state, TS_CARR_ON) && !ISSET(tp->t_cflag, CLOCAL)) { if (ISSET(tp->t_state, TS_ISOPEN)) { splx(s); return (EIO); } else if (flag & IO_NDELAY) { splx(s); error = EWOULDBLOCK; goto out; } else { /* Sleep awaiting carrier. */ error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,ttopen, 0); splx(s); if (error) goto out; goto loop; } } splx(s); /* * Hang the process if it's in the background. */ p = curproc; if (isbackground(p, tp) && ISSET(tp->t_lflag, TOSTOP) && (p->p_flag & P_PPWAIT) == 0 && (p->p_sigignore & sigmask(SIGTTOU)) == 0 && (p->p_sigmask & sigmask(SIGTTOU)) == 0 && p->p_pgrp->pg_jobc) { pgsignal(p->p_pgrp, SIGTTOU, 1); if (error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0)) goto out; goto loop; } /* * Process the user's data in at most OBUFSIZ chunks. Perform any * output translation. Keep track of high water mark, sleep on * overflow awaiting device aid in acquiring new space. */ while (uio->uio_resid > 0 || cc > 0) { if (ISSET(tp->t_lflag, FLUSHO)) { uio->uio_resid = 0; return (0); } if (tp->t_outq.c_cc > hiwat) goto ovhiwat; /* * Grab a hunk of data from the user, unless we have some * leftover from last time. */ if (cc == 0) { cc = min(uio->uio_resid, OBUFSIZ); cp = obuf; error = uiomove(cp, cc, uio); if (error) { cc = 0; break; } } /* * If nothing fancy need be done, grab those characters we * can handle without any of ttyoutput's processing and * just transfer them to the output q. For those chars * which require special processing (as indicated by the * bits in char_type), call ttyoutput. After processing * a hunk of data, look for FLUSHO so ^O's will take effect * immediately. */ while (cc > 0) { if (!ISSET(tp->t_oflag, OPOST)) ce = cc; else { ce = cc - scanc((u_int)cc, (u_char *)cp, (u_char *)char_type, CCLASSMASK); /* * If ce is zero, then we're processing * a special character through ttyoutput. */ if (ce == 0) { tp->t_rocount = 0; if (ttyoutput(*cp, tp) >= 0) { /* No Clists, wait a bit. */ ttstart(tp); if (error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybuf, 0)) break; goto loop; } cp++; cc--; if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) goto ovhiwat; continue; } } /* * A bunch of normal characters have been found. * Transfer them en masse to the output queue and * continue processing at the top of the loop. * If there are any further characters in this * <= OBUFSIZ chunk, the first should be a character * requiring special handling by ttyoutput. */ tp->t_rocount = 0; i = b_to_q(cp, ce, &tp->t_outq); ce -= i; tp->t_column += ce; cp += ce, cc -= ce, tk_nout += ce; tp->t_outcc += ce; if (i > 0) { /* No Clists, wait a bit. */ ttstart(tp); if (error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybuf, 0)) break; goto loop; } if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) break; } ttstart(tp); }out: /* * If cc is nonzero, we leave the uio structure inconsistent, as the * offset and iov pointers have moved forward, but it doesn't matter * (the call will either return short or restart with a new uio). */ uio->uio_resid += cc; return (error);ovhiwat: ttstart(tp); s = spltty(); /* * This can only occur if FLUSHO is set in t_lflag, * or if ttstart/oproc is synchronous (or very fast). */ if (tp->t_outq.c_cc <= hiwat) { splx(s); goto loop; } if (flag & IO_NDELAY) { splx(s); uio->uio_resid += cc; return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); } SET(tp->t_state, TS_ASLEEP); error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout, 0); splx(s); if (error) goto out; goto loop;}/* * Rubout one character from the rawq of tp * as cleanly as possible. */voidttyrub(c, tp) register int c; register struct tty *tp;{ register char *cp; register int savecol; int tabc, s; if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) return; CLR(tp->t_lflag, FLUSHO); if (ISSET(tp->t_lflag, ECHOE)) { if (tp->t_rocount == 0) { /* * Screwed by ttwrite; retype */ ttyretype(tp); return; } if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) ttyrubo(tp, 2); else { CLR(c, ~TTY_CHARMASK); switch (CCLASS(c)) { case ORDINARY: ttyrubo(tp, 1); break; case BACKSPACE: case CONTROL: case NEWLINE: case RETURN: case VTAB: if (ISSET(tp->t_lflag, ECHOCTL)) ttyrubo(tp, 2); break; case TAB: if (tp->t_rocount < tp->t_rawq.c_cc) { ttyretype(tp); return; } s = spltty(); savecol = tp->t_column; SET(tp->t_state, TS_CNTTB); SET(tp->t_lflag, FLUSHO); tp->t_column = tp->t_rocol; cp = tp->t_rawq.c_cf; if (cp) tabc = *cp; /* XXX FIX NEXTC */ for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc)) ttyecho(tabc, tp); CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_CNTTB); splx(s); /* savecol will now be length of the tab. */ savecol -= tp->t_column; tp->t_column += savecol; if (savecol > 8) savecol = 8; /* overflow screw */ while (--savecol >= 0) (void)ttyoutput('\b', tp); break; default: /* XXX */#define PANICSTR "ttyrub: would panic c = %d, val = %d\n" (void)printf(PANICSTR, c, CCLASS(c));#ifdef notdef panic(PANICSTR, c, CCLASS(c));#endif } } } else if (ISSET(tp->t_lflag, ECHOPRT)) { if (!ISSET(tp->t_state, TS_ERASE)) { SET(tp->t_state, TS_ERASE); (void)ttyoutput('\\', tp); } ttyecho(c, tp); } else ttyecho(tp->t_cc[VERASE], tp); --tp->t_rocount;}/* * Back over cnt characters, erasing them. */static voidttyrubo(tp, cnt) register struct tty *tp; int cnt;{ while (cnt-- > 0) { (void)ttyoutput('\b', tp); (void)ttyoutput(' ', tp); (void)ttyoutput('\b', tp); }}/* * ttyretype -- * Reprint the rawq line. Note, it is assumed that c_cc has already * been checked. */voidttyretype(tp) register struct tty *tp;{ register char *cp; int s, c; /* Echo the reprint character. */ if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) ttyecho(tp->t_cc[VREPRINT], tp); (void)ttyoutput('\n', tp); /* * XXX * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE * BIT OF FIRST CHAR. */ s = spltty(); for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_canq, cp, &c)) ttyecho(c, tp); for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_rawq, cp, &c)) ttyecho(c, tp); CLR(tp->t_state, TS_ERASE); splx(s); tp->t_rocount = tp->t_rawq.c_cc; tp->t_rocol = 0;}/* * Echo a typed character to the terminal. */static voidttyecho(c, tp) register int c; register struct tty *tp;{ if (!ISSET(tp->t_state, TS_CNTTB)) CLR(tp->t_lflag, FLUSHO); if ((!ISSET(tp->t_lflag, ECHO) && (!ISSET(tp->t_lflag, ECHONL) || c == '\n')) || ISSET(tp->t_lflag, EXTPROC)) return; if (ISSET(tp->t_lflag, ECHOCTL) && (ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' || ISSET(c, TTY_CHARMASK) == 0177)) { (void)ttyoutput('^', tp); CLR(c, ~TTY_CHARMASK); if (c == 0177) c = '?'; else c += 'A' - 1; } (void)ttyoutput(c, tp);}/* * Wake up any readers on a tty. */voidttwakeup(tp) register struct tty *tp;{ selwakeup(&tp->t_rsel); if (ISSET(tp->t_state, TS_ASYNC)) pgsignal(tp->t_pgrp, SIGIO, 1); wakeup((caddr_t)&tp->t_rawq);}/* * Look up a code for a specified speed in a conversion table; * used by drivers to map software speed values to hardware parameters. */intttspeedtab(speed, table) int speed; register struct speedtab *table;{ for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1);}/* * Set tty hi and low water marks. * * Try to arrange the dynamics so there's about one second * from hi to low water. * */voidttsetwater(tp) struct tty *tp;{ register int cps, x;#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) cps = tp->t_ospeed / 10; tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); x += cps; x = CLAMP(x, TTMAXHIWAT, TTMINHIWAT); tp->t_hiwat = roundup(x, CBSIZE);#undef CLAMP}/* * Report on state of foreground process group. */voidttyinfo(tp) register struct tty *tp;{ register struct proc *p, *pick; struct timeval utime, stime; int tmp; if (ttycheckoutq(tp,0) == 0) return; /* Print load average. */ tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100); if (tp->t_session == NULL) ttyprintf(tp, "not a controlling terminal\n"); else if (tp->t_pgrp == NULL) ttyprintf(tp, "no foreground process group\n"); else if ((p = tp->t_pgrp->pg_mem) == NULL) ttyprintf(tp, "empty foreground process group\n"); else { /* Pick interesting process. */ for (pick = NULL; p != NULL; p = p->p_pgrpnxt) if (proc_compare(pick, p)) pick = p; ttyprintf(tp, " cmd: %s %d [%s] ", pick->p_comm, pick->p_pid, pick->p_stat == SRUN ? "running" : pick->p_wmesg ? pick->p_wmesg : "iowait"); calcru(pick, &utime, &stime, NULL); /* Print user time. */ ttyprintf(tp, "%d.%02du ", utime.tv_sec, (utime.tv_usec + 5000) / 10000); /* Print system time. */ ttyprintf(tp, "%d.%02ds ", stime.tv_sec, (stime.tv_usec + 5000) / 10000);#define pgtok(a) (((a) * NBPG) / 1024) /* Print percentage cpu, resident set size. */ tmp = pick->p_pctcpu * 10000 + FSCALE / 2 >> FSHIFT; ttyprintf(tp, "%d%% %dk\n", tmp / 100, pick->p_stat == SIDL || pick->p_stat == SZOMB ? 0 :#ifdef pmap_resident_count pgtok(pmap_resident_count(&pick->p_vmspace->vm_pmap))#else pgtok(pick->p_vmspace->vm_rssize)#endif ); } tp->t_rocount = 0; /* so pending input will be retyped if BS */}/* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (P_SINTR == 0). * 4) Further ties are broken by picking the highest pid. */#define ISRUN(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL))#define TESTAB(a, b) ((a)<<1 | (b))#define ONLYA 2#define ONLYB 1#define BOTH 3static intproc_compare(p1, p2) register struct proc *p1, *p2;{ if (p1 == NULL) return (1); /* * see if at least one of them is runnable */ switch (TESTAB(ISRUN(p1), ISRUN(p2))) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: /* * tie - favor one with highest recent cpu utilization */ if (p2->p_estcpu > p1->p_estcpu) return (1); if (p1->p_estcpu > p2->p_estcpu) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * weed out zombies */ switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * pick the one with the smallest sleep time */ if (p2->p_slptime > p1->p_slptime) return (0); if (p1->p_slptime > p2->p_slptime) return (1); /* * favor one sleeping in a non-interruptible sleep */ if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0) return (1); if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */}/* * Output char to tty; console putchar style. */inttputchar(c, tp) int c; struct tty *tp;{ register int s; s = spltty(); if (ISSET(tp->t_state, TS_CARR_ON | TS_ISOPEN) != (TS_CARR_ON | TS_ISOPEN)) { splx(s); return (-1); } if (c == '\n') (void)ttyoutput('\r', tp); (void)ttyoutput(c, tp); ttstart(tp); splx(s); return (0);}/* * Sleep on chan, returning ERESTART if tty changed while we napped and * returning any errors (e.g. EINTR/ETIMEDOUT) reported by tsleep. If * the tty is revoked, restarting a pending call will redo validation done * at the start of the call. */intttysleep(tp, chan, pri, wmesg, timo) struct tty *tp; void *chan; int pri, timo; char *wmesg;{ int error; short gen; gen = tp->t_gen; if (error = tsleep(chan, pri, wmesg, timo)) return (error); return (tp->t_gen == gen ? 0 : ERESTART);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -