星期日, 7月 24, 2022

stty

類比 tcgetattr() 和 tcsetattr() 改變終端機屬性的指令。

顯示精簡屬性 (settings that deviate from sane values.):

$ stty
speed 38400 baud; line = 0;
-brkint -imaxbel iutf8

顯示所有屬性 (here carried out on a virtual console):

$ stty -a
speed 38400 baud; rows 25; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
  • 第 1 行:終端機 line speed、window size、和 line discipline (0 代表 N_TTY,new line discipline)。
  • 再來 3 行:終端機特殊字元設定。min 和 time 用在 noncanonical 模式輸入。
  • 剩下依序是 c_cflag, c_iflag, c_oflag, and c_lflag,前置 hyphen (-) 代表關閉,否則啟用。

將中斷字元改為 Ctrl-L:

$ stty intr Ctrl-L
$ stty intr 014
$ stty intr 0xC
$ stty intr <實際字元>

如果要設定的字元會被 shell 或終端機驅動程式特別解釋,需要前置 literal next character (通常是 ^V):

$ stty intr Ctrl-VCtrl-L

啟用或停用 TOSTOP

$ stty tostop
$ stty -tostop

有時程式改變終端機屬性而當掉卡住,可輸入:

Ctrl-J stty sane Ctrl-J

來回復 terminal flags 和特殊字元為 reasonable 狀態。Ctrl-J (ASCII 10) 是換行。在有些模式,終端機驅動程式不再對應 Enter 鍵 (ASCII 13) 到換行字元。第一個 Ctr-J 確保全新的指令行。

操作在指定終端機,不是 standard input,需要有權限:

# stty -a -F /dev/tty3
# stty -a < /dev/tty3

將中斷字元改為 Ctrl-L:

$ stty intr Ctrl-L
$ stty
speed 38400 baud; line = 0;
intr = ^L;

開始 sleep,發現 Ctrl-C 沒作用,只是 echoed:

$ sleep 10
^C

Ctrl-L 結束 sleep,顯示 termination status:

$ echo $?
130

130 表示程式由 signal number 130 – 128 = 2 (SIGINT) 所殺。

然後停用中斷字元:

$ stty intr undef
$ stty
speed 38400 baud; line = 0;
intr = <undef>;

現在 Ctrl-C 和 Ctrl-L 都不會產生 SIGINT signal,必須用 Ctrl-\ 代替來結束程式:

$ sleep 10
^C^L Control-C and Control-L are simply echoed
Type Control-\ to generate SIGQUIT
Quit
$ stty sane Return terminal to a sane state

參考

  1. TLPI §62

星期日, 7月 10, 2022

kernel Link Layer Multicast

struct dev_mc_list *mc_list; // multicast 位址列表,透過 dev_mc_add() 和 dev_mc_delete() 新增和移除。

int mc_count; // multicast 位址數目

int allmult; // 接收所有 multicast 封包

星期日, 7月 03, 2022

Simulator and Emulator

Emulation 和另一個系統有完全相同的行為而能取代它,或許有速度上的差異。Simulation 是嘗試預測某些系統行為而建立的模型。

星期六, 7月 02, 2022

TERMINALS

TLPI §62 的筆記整理

終端機是顯示和輸入的裝置,早期 UNIX 系統是用 RS-232 序列埠連接,顯示器是 24x80 CRT 黑白螢幕。終端機的螢幕控制的字元順序,例如移動游標到行首或頁首,有 VT-100、ANSI 等等標準,所以發展了表格資料庫 termcap 和 terminfo 和函式庫 curses。更早,終端機是紙本電傳打字機 (teletype),所以用 /dev/ttyn 來代表終端機。在現代高效能圖形顯示器,變成 X 視窗系統下的終端機視窗 (terminal emulator,一個 xterm 或類似的 pseudoterminal [TLPI §64]?)。

在 Linux,/dev/ttyn 是 virtual console。virtual console 和 terminal 有什麼不同?

每個終端機有對應的終端機驅動程式來處理輸入和輸出。

當進行輸入,設定 ICANON 決定驅動程式運作在 Canonical 模式 (預設) 或 Noncanonical 模式:

  • Canonical 模式:read() 以「行」為單位,並且 Enter 換行前可以編輯。如果 read() 要求較少位元,需要下個 read() 讀取。
    • 「行」的結束字元有 NL、EOL、EOL2 (如果 IEXTEN)、和 CR (如果 ICRNL),這些結束字元也會傳給讀取程式。另外,在行首以外的 EOF 也會結束一行,但不會傳給讀取程式。A read() may also terminate if interrupted by a signal handler and restarting of system calls is not enabled for this signal (Section 21.5)。中斷會清掉輸入 queue,無論程式是否要 caught 中斷或忽略,啟用 NOFLSH 可避免。
    • 「行」編輯功能有 ERASE、KILL、和 WERASE (如果 IEXTEN) 可修改輸入。
    • 如果 IEXTEN,啟用 REPRINT 和 LNEXT。
  • Noncanonical 模式:read() 指定等多久或幾 byte 結束,分別由 c_cc[] 的 TIME [VTIME] 和 MIN [VMIN] 決定。 不一行一行收集,例如用在程式 vi、more、less 等,讀取每一單一字元,沒有特殊輸入處理,不用等換行。
    • MIN == 0, TIME == 0 (polling read):馬上結束,回讀取的字元數目。 類似設 O_NONBLOCK (Section 5.9),但讀沒資料時是回 -1 加 error EAGAIN。
    • MIN > 0, TIME == 0 (blocking read):等候直到滿足 MIN 或請求數。MIN 設為 1 讓程式等候任何按鍵,CPU 不用忙於 polling 迴圈,可使用 Chapter 63 的techniques。
    • MIN == 0, TIME > 0 (read with timeout):任何讀到或 TIME 到結束。適合用在 serial device (e.g., a modem),送資料後等候回應,使用 timeout 避免等不到回應卡住。
    • MIN > 0, TIME > 0 (read with interbyte timeout):至少會讀到 1 byte,每有 1 個 byte 後重新 TIME 計數,直到逾時或滿足 MIN 或請求數。適合處理產生 escape sequences 的終端機按鍵,例如左箭頭鍵會很快的產生 3-character sequence Escape followed by OD,程式需要識別是按左箭頭、還是分別按了這三鍵,可透過設定小的 TIME,如 0.2 秒。

終端機驅動程式同時解釋一些特殊字元,例如中斷字元 (一般 Ctrl-C)、EOF 字元 (一般 Ctrl-D)。在 noncanonical 模式通常關閉部份或全部這些特殊字元處理。

終端機驅動程式有一個輸入 queue (從終端機輸入到 process 讀取) 和一個輸出 queue (從 process 寫入到終端機顯示)。如果啟用 echoing,任何輸入字元自動 appends 一份到輸出 queue 的尾巴,輸入字元才會顯示在 terminal.

終端機
終端機驅動程式
程式
輸入 queue


輸出 queue



Linux kernel 限制輸入 queue 4096 bytes。輸出 queue 也是 4096 bytes,但應用程式不用考量這個限制,如果輸出超過驅動程式可以處理的速度,kernel suspends 寫入 process 的執行,直到輸出 queue 有空間。

在 Linux,呼叫 ioctl(fd, FIONREAD, &cnt) 取得輸入 queue 未讀的 byte 數。

取得和設定終端機屬性

#include <termios.h>
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
  • 成功回 0,失敗回 –1。通常先 tcgetattr() 取得屬性後作必要的修改後 tcsetattr() 設定。tcsetattr() 只要有部份設定成功就回 0,當有多項改變時,可能需要再 tcgetattr() 來比較。在 Linux 和許多 UNIX,是使用 ioctl() 的程式庫函式。
  • fd 是參照到終端機的 file descriptor,不是的話回 error ENOTTY。 
  • struct termios 紀錄終端機屬性

struct termios 紀錄終端機屬性:

struct termios {
  tcflag_t c_iflag; // Terminal Input flags
  tcflag_t c_oflag; // Terminal Output flags
  tcflag_t c_cflag; // Hardware Control flags of the terminal line
  tcflag_t c_lflag; // Local modes,關於輸入 line edit 部份,例如 ECHO
  cc_t c_line;      // Line discipline (nonstandard)
  cc_t c_cc[NCCS];  // Terminal special characters,見 §62.4
  speed_t c_ispeed; // Input speed,Linux 沒用,Linux 方式見 §62.7
  speed_t c_ospeed; // Output speed,Linux 沒用,Linux 方式見 §62.7
};

前 4 個 flag 欄位是 bit masks 控制各種終端機驅動程式的操作。Default column shows the default settings for a virtual console login。

c_iflag

  • BRKINT:預設 on。IGNBRK 未設時,按 BREAK 時送 Signal interrupt (SIGINT)。在 virtual console,BREAK 是按 Ctrl-Break。大部分傳統 dumb 終端機有 BREAK 鍵,按此鍵實際上不產生字元,但一系列 0 bits is sent to the terminal driver for a specified length of time, typically 0.25 or 0.5 seconds (i.e., longer than the time required to transmit a single byte). 許多 UNIX 系統,BREAK 是要告訴遠端主機換 line speed (baud),直到登入提示出現。
  • ICRNL:預設 on。 Map CR to NL on input on
  • IGNBRK:預設 off。 Ignore BREAK condition off
  • IGNCR:預設 off。 Ignore CR on input off
  • IGNPAR:忽略 parity 錯誤字元,預設不忽略。
  • IMAXBEL:Ring bell 當輸入 queue 滿了,預設啟用,Linux 忽略。
  • INLCR:預設 off。 Map NL to CR on input off
  • INPCK:輸入 parity 檢查,預設停用。
  • ISTRIP:Strip high bit (bit 8) from input characters,預設不。
  • IUTF8:預設停用。 Input is UTF-8 (since Linux 2.6.4) Setting the IUTF8 flag enables cooked mode (Section 62.6.3) to correctly handle UTF-8 input when performing line editing.
  • IUCLC:預設 off。 Map uppercase to lowercase on input (if IEXTEN also set) off
  • IXANY:預設 off。 Allow any character to restart stopped output off
  • IXOFF:預設 off。 Enable start/stop input flow control off
  • IXON:預設 on。Enable start/stop output flow control on
  • PARMRK:送 parity 錯誤字元前置 0377 + 0 (ISTRIP 沒設時,0377 + 0377 + 0),預設 off。

c_oflag

  • BSDLY:預設 BS0。 Backspace delay mask (BS0, BS1) BS0
  • CRDLY:預設 CR0。 CR delay mask (CR0, CR1, CR2, CR3) CR0
  • FFDLY:預設 FF0。 Form-feed delay mask (FF0, FF1) FF0
  • NLDLY:預設 NL0。 Newline delay mask (NL0, NL1) NL0
  • OCRNL:預設 off。 Map CR to NL on output (see also ONOCR) off
  • OFDEL:預設 off。 Use DEL (0177) as fill character; otherwise NUL (0) off
  • OFILL:預設 off。 Use fill characters for delay (rather than timed delay) off
  • OLCUC:預設 off。 Map lowercase to uppercase on output off
  • ONLCR:預設 on。 Map NL to CR-NL on output on
  • ONLRET:預設 off。 Assume NL performs CR function (move to start of line) off
  • ONOCR:預設 off。 Don’t output CR if already at column 0 (start of line) off
  • OPOST:輸出 postprocessing,預設啟用,讓 c_oflag 其它 flag 有效。
  • TABDLY:預設 TAB0。 Horizontal-tab delay mask (TAB0, TAB1, TAB2, TAB3) TAB0
  • VTDLY:預設 VT0。 Vertical-tab delay mask (VT0, VT1) VT0

c_cflag

  • CBAUD:預設 B38400。 Baud (bit rate) mask (B0, B2400, B9600, and so on) B38400
  • CBAUDEX:預設 off。 Extended baud (bit rate) mask (for rates > 38,400) off
  • CIBAUD:預設 (off)。 Input baud (bit rate), if different from output (unused) (off)
  • CLOCAL:預設 off。 Ignore modem status lines (don’t check carrier signal) off
  • CMSPAR:預設 off。 Use “stick” (mark/space) parity off
  • CREAD:預設 on。 Allow input to be received on
  • CRTSCTS:預設 off。 Enable RTS/CTS (hardware) flow control off
  • CSIZE:預設 CS8。 Character-size mask (5 to 8 bits: CS5, CS6, CS7, CS8) CS8
  • CSTOPB:預設 off。 Use 2 stop bits per character; otherwise 1 off
  • HUPCL:預設 on。 Hang up (drop modem connection) on last close on
  • PARENB:啟用 parity,預設停用。
  • PARODD:使用 odd parity,否則 even parity,預設 even。

c_lflag

  • ECHO:顯示輸入字元,預設啟用,不論 canonical 模式與否都有效用。在讀取密碼、或 vi command mode 會停用。
  • ECHOCTL:顯示 tab、換行、START 和 STOP 以外的控制字元 (32 以下和 127),預設 on,ECHO 時有效。
  • ECHOE:ERASE 原本字元改為顯示 backspace-space-backspace,預設啟用。canonical 模式時有效。
  • ECHOK:KILL 原本字元改為顯示一行刪除,預設啟用。canonical 模式且 ECHOKE 時有效。
  • ECHOKE:KILL 後不顯示換行,預設啟用。否則在 ECHOK 時會顯示換行。
  • ECHONL:預設 off。 Echo NL (in canonical mode) even if echoing is disabled off
  • ECHOPRT:預設 off。 Echo deleted characters backward (between \ and /) off
  • FLUSHO:預設 -。 Output is being flushed (unused) -
  • ICANON:canonical 模式 (一行一行處理) 輸入,預設啟用。Input is gathered into lines, and special interpretation of the EOF, EOL, EOL2, ERASE, LNEXT, KILL, REPRINT, and WERASE characters is enabled (but note the effect of the IEXTEN flag described below)
  • IEXTEN:啟用 extended processing of 輸入字元,預設啟用。This flag (as well as ICANON) must be set in order for the following characters to be interpreted: EOL2, LNEXT, REPRINT, and WERASE. The IEXTEN flag must also be set for the IUCLC flag to be effective.
  • ISIG:預設 on。 Enable signal-generating characters (INTR, QUIT, SUSP) on
  • NOFLSH:INTR、QUIT、和 SUSP 時不忽略輸入輸出 queue 內的資料,預設停用會忽略。
  • PENDIN:預設 (off)。 Redisplay pending input at next read (not implemented) (off)
  • TOSTOP:預設 off。 Generate SIGTTOU for background output (Section 34.7.1) off
  • XCASE:預設 (off)。 Canonical upper/lowercase presentation (unimplemented) (off)

有些 flags 是為了功能有限的古老終端機,沒什麼用處。例如 IUCLC、OLCUC、和 XCASE 只用在只能顯示大寫字母的終端機。ECHOPRT flag was also designed for limited-capability terminals.

The various delay masks are also historical, allowing for terminals and printers that took longer to echo characters such as carriage return and form feed. The related OFILL and OFDEL flags specified how such a delay was to be performed. 大部分在 Linux 沒用。One exception is the TAB3 setting for the TABDLY mask, which causes tab characters to be output as (up to eight) spaces.

c_line:終端機的 line discipline。For the purposes of programming terminal emulators, the line discipline will always be set to N_TTY, the so-called new discipline, a component of the kernel terminal-handling code that implements canonical mode I/O processing. Setting the line discipline can be relevant when programming serial lines.

c_cc[] 是終端機特殊字元 (interrupt, suspend, and so on) as well as fields controlling the operation of noncanonical mode input. We describe the terminal special characters in Section 62.4.

tcsetattr() 設定終端機屬性,optional_actions 決定改變何時生效:

  • TCSANOW:立即生效。
  • TCSADRAIN:目前輸出 queue 都送出後生效。通常改變輸出需要,避免影響已 queue 但未顯示的輸出。
  • TCSAFLUSH:如同 TCSADRAIN 外,生效時再 discard pending 輸入。例如用在讀取密碼時,要關閉 echoing 並避免使用者事先輸入。

TLPI §34.7.2,如果在背景呼叫 tcsetattr(),終端機驅動程式 suspends the process group by delivering a SIGTTOU signal。如果在 orphaned process group 呼叫,fails with the error EIO。對 tcflush()、tcflow()、tcsendbreak()、和 tcdrain() 也是一樣。

Terminal Special Characters 和 c_cc[]

特殊字元c_cc[]預設相關 termios flags說明
CR(不能變)^MICANON
IGNCR:忽略輸入
ICRNL:輸入轉成 NL
OPOST
OCRNL
ONOCR
Carriage return。輸出將終端機游標移到行首。
DISCARDVDISCARD^O
toggle 忽略輸出,在 Linux 沒作用。
EOFVEOF^DICANONEnd-of-file
EOLVEOL停用ICANONEnd-of-line
EOL2VEOL2停用ICANON 且 IEXTEN另一個 end-of-line
ERASEVERASE^?ICANON消除前一字元
INTRVINTR^CISIG送 SIGINT
KILLVKILL^UICANON消除行
LNEXTVLNEXT^VICANON 且 IEXTENLiteral next,下個字元視為一般字元。
NL(不能變)^JICANON, INLCR, ECHONL,
OPOST, ONLCR, ONLRET
Newline,輸出將游標往下移一行。
QUITVQUIT^\ISIG送 SIGQUIT signal
REPRINTVREPRINT^RICANON, IEXTEN, ECHO重印輸入行 (輸入可能混輸出)
STARTVSTART^QIXON, IXOFFStart output
STOPVSTOP^SIXON, IXOFFStop output
SUSPVSUSP^ZISIG送 SIGTSTP signal
WERASEVWERASE^WICANON 且 IEXTENErase word
-VTIME?非 ICANONTIME,單位十分之一秒。
-VMIN?非 ICANONMIN

停用終端機特殊字元透過設為 fpathconf(fd, _PC_VDISABLE) 回傳的值,大部分 UNIX 實作是 0。

終端機特殊字元,除了換行的 CR、EOL、EOL2、和 NL 外,不會傳給讀取的程式。

  • EOL 和 EOL2:額外的行結束字元。用在 telnet,設定為跳脫字元 (通常是 Ctrl-],或在 rlogin 模式時 ~),讓 telnet 在讀取時能夠立即處理。
  • NL:換行。In canonical mode, this character terminates an input line. If the OPOST and ONLCR (map NL to CR-NL) flags are set (the default), then, on output, a newline character is mapped to the 2-character sequence CR plus NL. (The combination of the ICRNL and ONLCR flags means that an input CR is converted to a NL, and then echoed as CR plus NL.)
  • START and STOP:the start output and stop output characters, which operate if the IXON (enable start/stop output control) flag is enabled (the default). (The START and STOP characters are not honored by some terminal emulators.) Typing the STOP character suspends terminal output. If the IXOFF flag is set and the terminal input queue is full, then the terminal driver automatically sends a STOP character to throttle the input. Typing the START character causes terminal output to resume after previously being stopped by the STOP character. If the IXOFF (enable start/stop input control) flag is set (this flag is disabled by default) and the terminal driver had previously sent a STOP character because the input queue was full, the terminal driver automatically generates a START character when space once more becomes available in the input queue. If the IXANY flag is set, then any character, not just START, may be typed in order to restart output (and that character is similarly not passed to the reading process). The START and STOP characters are used for software flow control in either direction between the computer and the terminal device. One function of these characters is to allow users to stop and start terminal output. This is output flow control, as enabled by IXON. However, flow control in the other direction (i.e., control of input flow from the device to the computer, as enabled by IXOFF) is also important when, for example, the device in question is a modem or another computer. Input flow control makes sure that no data is lost if the application is slow to handle input and the kernel buffers fill up. With the higher line speeds that are nowadays typical, software flow control has been superseded by hardware (RTS/CTS) flow control, whereby data flow is enabled and disabled using signals sent via separate wires on the serial port. (RTS stands for Request To Send, and CTS stands for Clear To Send.)

the magic SysRq key; see the kernel source file Documentation/sysrq.txt.

62.5 Terminal Flags

struct termios 的 4 個 flag 欄位。

許多 shell 有提供指令行編輯功能進行 flag manipulations,用 stty 改設定未必有效。為了 circumvent this behavior,必須關閉 shell 指令行編輯功能,例如 invoke bash 加 ––noediting 選項。

62.6 Terminal I/O Modes

terminal modes—cooked, cbreak, and raw are emulated on modern UNIX systems by setting appropriate values in the termios structure.

62.6.3 Cooked, Cbreak, and Raw Modes

Cooked mode was essentially canonical mode with all of the default special charac- ter processing enabled (i.e., interpretation of CR, NL, and EOF; enabling of line edit- ing; handling of signal-generating characters; ICRNL; OCRNL; and so on).

Raw mode was the converse: noncanonical mode, with all input and output processing, as well as echoing, switched off. (An application that needed to ensure that the terminal driver makes absolutely no changes to the data transferred across a serial line would use this mode.)

Cbreak mode was intermediate between cooked and raw modes. Input was noncanonical, but signal-generating characters were interpreted, and the various input and output transformations could still occur (depending on individual flag settings). Cbreak mode did not disable echoing, but applications employing this mode would usually disable echoing as well. Cbreak mode was useful in screen- handling applications (such as less) that permitted character-by-character input, but still needed to allow interpretation of characters such as INTR, QUIT, and SUSP.

62.7 Terminal Line Speed (Bit Rate)

62.8 Terminal Line Control

62.9 Terminal Window Size

62.10 Terminal Identification

62.11 Summary

62.12 Exercises

參考

  1. TLPI §62
  • https://en.wikipedia.org/wiki/Computer_terminal
  • Terminal Recorders:紀錄和播放指令動作。