天天看點

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

TTY解密(The TTY demystified)

The TTY subsystem is central to the design of Linux, and UNIX in general. Unfortunately, its importance is often overlooked, and it is difficult to find good introductory articles about it. I believe that a basic understanding of TTYs in Linux is essential for the developer and the advanced user.

在Linux系統和UNIX系統的設計中,TTY子系統都是處于重要地位的。但不幸的是,它的重要性往往被忽視了,并且很難找到好的介紹TTY系統的資料。我認為無論對Linux系統的開發者還是是進階使用者而言,都有必要對TTY系統有一個基本的認識。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

                                                                            早期的電傳打字機

Beware, though: What you are about to see is not particularly elegant. In fact, the TTY subsystem — while quite functional from a user's point of view — is a twisty little mess of special cases. To understand how this came to be, we have to go back in time.

    然而,不得不提的是:TTY子系統并不是你想的那樣優雅和簡潔。事實上,盡管使用者從功能性的角度來看,它功能非常的強大以及實用,但是在一些特殊情況下,關于TTY子系統的概念(比如通過網絡登入系統)甚至有些混亂。為了解釋這一切到底是為什麼,我們必須回到過去看看TTY的發展曆史。

History 曆史

    In 1869, the stock ticker was invented. It was an electro-mechanical machine consisting of a typewriter, a long pair of wires and a ticker tape printer, and its purpose was to distribute stock prices over long distances in realtime. This concept gradually evolved into the faster, ASCII-based teletype. Teletypes were once connected across the world in a large network, called Telex, which was used for transferring commercial telegrams, but the teletypes weren't connected to any computers yet.

    早在1869年,人們發明了股票報價機。它是由一個打字機通過兩根很長的電線與遠端的紙條列印機相連組成的電子裝置,它被發明的目的主要是向千裡之外的人們實時地釋出股票價格。逐漸的,股票報價機被演變成了電傳打字機,電傳列印機的速度更快,并且它是基于ASCII編碼的。世界上的所有電傳打字機曾一度被連接配接在一起,組成了一個叫做Telex的大型網絡,被廣泛的用于傳輸商業電報,但是那個時候的電傳打字機是不與任何計算機相連的。

    Meanwhile, however, the computers — still quite large and primitive, but able to multitask — were becoming powerful enough to be able to interact with users in realtime. When the command line eventually replaced the old batch processing model, teletypes were used as input and output devices, because they were readily available on the market.

    同時期的計算機卻依然是個龐然大物,并且還處于很原始/簡單的發展階段,但是卻已經可以支援多任務處理了 -- 多任務功能已經足以使計算機可以實時地同多人互動作業。再後來,當指令行逐漸地替代了之前的批處理模式時,電傳打字機被用來作為計算機的輸入/輸出裝置,因為市面上有很多現成的電傳打字機可以使用,且價格低廉。

     There was a plethora of teletype models around, all slightly different, so some kind of software compatibility layer was called for. In the UNIX world, the approach was to let the operating system kernel handle all the low-level details, such as word length, baud rate, flow control, parity, control codes for rudimentary line editing and so on. Fancy cursor movements, colour output and other advanced features made possible in the late 1970s by solid state video terminals such as the VT-100, were left to the applications.

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

DEC VT100 terminal

    然而市面充斥着各種各樣的電傳打字機,他們或多或少有些不同之處,是以急切的需要一個軟體相容層(software compatibility layer)來處理這些電傳打字機的不同細節特性。在UNIX系統中,解決的辦法是讓作業系統核心來處理這些繁雜的終端細節,比如字長(word length),波特率(baud rate),流控制(flow control),奇偶校驗(parity),以及一些基本行編輯(line editing)的控制碼(control codes),當然還包括很多其它終端特性。到了上個世紀70年代後期,視訊終端(video terminals)的出現(比如VT-100視訊終端),使得豔麗的光标移動,顔色輸出以及其他終端進階特性成為可能,同樣的,這些新的終端特性也完全可以由核心來配置決定。

    In present time, we find ourselves in a world where physical teletypes and video terminals are practically extinct. Unless you visit a museum or a hardware enthusiast, all the TTYs you're likely to see will be emulated video terminals — software simulations of the real thing. But as we shall see, the legacy from the old cast-iron beasts is still lurking beneath the surface.

    發展到現在,生活中我們已經幾乎看不到真實的電傳打字機和視訊終端了,除非你去博物館或者去硬體收集發燒友那裡才能看到。現在你所能見到的終端都是模拟的,模拟的視訊終端(emulated video terminals) -- 也就是用軟體模拟真實的終端。但是在使用模拟終端的過程中,我們會發現,明白一些看似很古老的概念對于我們的使用和學習大有裨益。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

    A user types at a terminal (a physical teletype). This terminal is connected through a pair of wires to a UART (Universal Asynchronous Receiver and Transmitter) on the computer. The operating system contains a UART driver which manages the physical transmission of bytes, including parity checks and flow control. In a naïve system, the UART driver would then deliver the incoming bytes directly to some application process. But such an approach would lack the following essential features:

    使用者通過終端(實體電傳打字機)鍵入資料資訊傳遞給計算機。終端是通過兩根串行線與計算機的UART(通用異步接收發器)相連的,使用者鍵入的資訊就是通過這兩根串行線傳遞給計算機。而作業系統的UART驅動程式是專門處理這些資料資訊的實體傳輸的,包括對這些資料進行奇偶校驗和流控制。在早期的系統中,UART驅動程式會直接将使用者鍵入的這些資料資訊不做任何處理地傳遞給應用程式,但是這樣不夠靈活,會導緻失去以下很重要的幾個特性:

Line editing. Most users make mistakes while typing, so a backspace key is often useful. This could of course be implemented by the applications themselves, but in accordance with the UNIX design philosophy, applications should be kept as simple as possible. So as a convenience, the operating system provides an editing buffer and some rudimentary editing commands (backspace, erase word, clear line, reprint), which are enabled by default inside the line discipline. Advanced applications may disable these features by putting the line discipline in raw mode instead of the default cooked (or canonical) mode. Most interactive applications (editors, mail user agents, shells, all programs relying on curses or readline) run in raw mode, and handle all the line editing commands themselves. The line discipline also contains options for character echoing and automatic conversion between carriage returns and linefeeds. Think of it as a primitive kernel-level sed(1), if you like.

行編輯功能。舉個大家習以為常的例子: 我們平時在打字過程中,肯定有打“錯字”的時候,那麼這個時候倒退鍵(backspace)就格外有用了。諸如此類的功能,應用軟體本身當然是可以實作的,但是這有悖于UNIX作業系統的設計原則: 應用軟體應該盡可能的簡單。是以為了友善,作業系統在終端子系統中專門加入了線路規程(line discipline)中間層,線路規程提供有編輯緩沖區(輸入隊列和輸出隊列)使得行編輯以及很多基本的編輯指令(backspace倒退,erase word删除單詞,clear line删除整行,reprint)成為可能。當然了,上層的應用程式可以通過設定線路規程為原始模式(非規範模式輸入處理)來取消這些功能(線路規程預設的模式是規範模式輸入處理,這種模式下支援行編輯以及編輯指令)。絕大多數的互動式應用程式(例如編輯器,電子郵件,shell,以及很多依賴于curses和readline的應用程式)都是運作在原始模式下,也就是說由它們自己來處理編輯指令。線路規程還包括字元回顯功能(stty echo),特殊字元間的轉換功能(例如在回車和換行之間自動轉換 -- 不同的作業系統對這個兩個字元有不同的處理http://www.cnblogs.com/clarkchen/archive/2011/06/02/2068609.html)。可以把線路規程想象成核心級的sed工具(sed是一個流編輯器應用軟體)。

    Incidentally, the kernel provides several different line disciplines. Only one of them is attached to a given serial device at a time. The default discipline, which provides line editing, is called N_TTY (drivers/char/n_tty.c, if you're feeling adventurous). Other disciplines are used for other purposes, such as managing packet switched data (ppp, IrDA, serial mice), but that is outside the scope of this article.

    值得一提的是,作業系統核心一般都會提供幾個不同的線路規程子產品。每一種串行裝置對應一種線路規程。預設的線路規程提供行編輯功能,被稱為N_TTY(drivers/char/n_tty.c)。其餘的線路規程有其特有的功能,比如用來管理資料包資料交換(ppp,IrDA,serial mice),不過這裡不做介紹。

Session management. The user probably wants to run several programs simultaneously, and interact with them one at a time. If a program goes into an endless loop, the user may want to kill it or suspend it. Programs that are started in the background should be able to execute until they try to write to the terminal, at which point they should be suspended. Likewise, user input should be directed to the foreground program only. The operating system implements these features in the TTY driver (drivers/char/tty_io.c).

會話管理。考慮下面幾個情景:(1)一個使用者有時想同時運作幾個程式,但是某一時刻隻能與其中的一個互動。(2)如果一個程式陷入死循環,那麼使用者可能想将其殺死(Ctrl+C或者kill -9)或者挂起(Ctrl+Z)。(3)當一個背景程序試圖往它的控制終端(該控制終端的設定中應該包括一項:不允許背景作業往它的控制終端寫資料,可通過stty tostop來設定 APUE9.8)寫資料時(或者讀終端資料),作業系統就會将其挂起(終端驅動程式向背景作業發送SIGTTIN或者SIGTTOU信号,這兩個信号會暫時停止此背景作業 APUE 9.8)。(4)此外,使用者通過終端輸入的資訊應該隻能直接發送給前台程序。諸如此類的功能,作業系統是在TTY驅動程式中實作的(drivers/char/tty_io.c)。

    An operating system process is "alive" (has an execution context), which means that it can perform actions. The TTY driver is not alive; in object oriented terminology, the TTY driver is a passive object. It has some data fields and some methods, but the only way it can actually do something is when one of its methods gets called from the context of a process or a kernel interrupt handler. The line discipline is likewise a passive entity.

    我們知道,隻有當一個程序的狀态是“活躍的(alive)”時,它才能執行相應的動作。然而TTY驅動程式的狀态卻不是處于“活躍”狀态,如果用面向對象的術語來說的話,就是TTY驅動程式是個被動對象,它有屬于自己的資料和方法,隻有當程序調用這些方法或者發生核心中斷時這些方法才會被執行。同樣的,線路規程也是個被動對象。

    Together, a particular triplet of UART driver, line discipline instance and TTY driver may be referred to as a TTY device, or sometimes just TTY. A user process can affect the behaviour of any TTY device by manipulating the corresponding device file under /dev. Write permissions to the device file are required, so when a user logs in on a particular TTY, that user must become the owner of the device file. This is traditionally done by the login(1) program, which runs with root privileges.

    是以,通常所說的TTY裝置(或者直接簡稱TTY)就是由UART驅動,線路規程和TTY驅動程式三部分組成。通常系統啟動時,會自動通過login程式(root權限)讓使用者登入終端,使使用者擁有該終端的所有權限,那麼使用者就可以根據/dev目錄下的相應tty檔案來操作該TTY裝置了。

The physical line in the previous diagram could of course be a long-distance phone line:

在前面的圖中,實體線(physical line)當然也可以是距離非常遠的電話線(通過數據機相連),如下圖所示。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

This does not change much, except that the system now has to handle a modem hangup situation as well.

其實并沒有改變多少,隻是作業系統需要額外處理數據機(modem)斷開連接配接這種情況(如果終端接口檢測到數據機斷開連接配接,則将挂斷信号SIGHUP發送給控制程序(會話首程序)APUE 9.6)。

Let's move on to a typical desktop system. This is how the Linux console works:

現在讓我們來看在一個典型的桌面系統中linux的控制台子系統是如何工作的:

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

    The TTY driver and line discipline behave just like in the previous examples, but there is no UART or physical terminal involved anymore. Instead, a video terminal (a complex state machine including a frame buffer of characters and graphical character attributes) is emulated in software, and rendered to a VGA display.

    可以看到,TTY驅動程式和線路規程依然保留使用,但是已經不存在UART驅動程式或者實體終端了。取而代之的卻是一個軟體模拟的視訊終端(真實的視訊終端是一個複雜的狀态機,擁有字元圖像緩沖功能等特性),由鍵盤輸入内容,最終顯示在VGA顯示器上。

    The console subsystem is somewhat rigid. Things get more flexible (and abstract) if we move the terminal emulation into userland. This is how xterm(1) and its clones work:

    如果能将終端模拟器提取到使用者空間,那麼控制台子系統就變得更靈活了(當然也變得更加的抽象了)。大家熟知的xterm就是這麼工作的:

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

    To facilitate moving the terminal emulation into userland, while still keeping the TTY subsystem (session management and line discipline) intact, the pseudo terminal or pty was invented. And as you may have guessed, things get even more complicated when you start running pseudo terminals inside pseudo terminals, à la screen(1) or ssh(1).

    為了實作将終端模拟器提取到使用者空間,而又不改變TTY子系統(包括會話管理和線路規程),僞終端(或稱為pty)被開發出來廣泛使用。也許你也可以猜到,當你在僞終端中再打開僞終端的話,那麼事情就變得越來越複雜了。典型的程式有screen和ssh。

Now let's take a step back and see how all of this fits into the process model.

現在讓我們來看看這一切是怎麼遵循作業系統程序模型的。

A Linux process can be in one of the following states:

在linux作業系統中,一個程序的狀态隻可能是以下狀态的一種:

R Running or runnable (on run queue)

D Uninterruptible sleep (waiting for some event)

S Interruptible sleep (waiting for some event or signal)

T Stopped, either by a job control signal or because it is being traced by a debugger.

Z Zombie process, terminated but not yet reaped by its parent.

R 正在運作或者說是可運作的(正處于運作隊列中等待)

D 不可中斷睡眠狀态(正在等待某個事件的發生)

S 可中斷睡眠狀态(正在等待某個事件的發生或者信号)

T 停止(挂起)狀态,也許是收到作業控制信号的緣故,也有可能是因為該程序正在被調試器調試

Z 僵屍程序,指的是一個程序已經終止,但是其系統資源還未被其父程序收回

    By running ps l, you can see which processes are running, and which are sleeping. If a process is sleeping, the WCHAN column ("wait channel", the name of the wait queue) will tell you what kernel event the process is waiting for.

    通過運作ps l指令,可以觀察到哪些程序正在運作,哪些程序處于睡眠狀态。如果一個程式處于休眠狀态,那麼WCHAN列(WCHAN是“wait channel”的縮寫,代表的是核心的等待隊列)會告訴你該程序是因為哪個核心函數而處于休眠狀态。

$ ps l

F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND

0   500  5942  5928  15   0  12916  1460 wait   Ss   pts/14     0:00 -/bin/bash

0   500 12235  5942  15   0  21004  3572 wait   S+   pts/14     0:01 vim index.php

0   500 12580 12235  15   0   8080  1440 wait   S+   pts/14     0:00 /bin/bash -c (ps l) >/tmp/v727757/1 2>&1

0   500 12581 12580  15   0   4412   824 -      R+   pts/14     0:00 ps l

    The "wait" wait queue corresponds to the wait(2) syscall, so these processes will be moved to the running state whenever there's a state change in one of their child processes. There are two sleeping states: Interruptible sleep and uninterruptible sleep. Interruptible sleep (the most common case) means that while the process is part of a wait queue, it may actually also be moved to the running state when a signal is sent to it. If you look inside the kernel source code, you will find that any kernel code which is waiting for an event must check if a signal is pending after schedule() returns, and abort the syscall in that case.

    列出的幾個“wait”對應的是wait(2)系統調用,是以當這些程序的任一個子程序狀态改變時,那麼程序的狀态就會從休眠狀态轉換成運作狀态。通常所說的睡眠狀态在linux系統中包括兩種:可中斷睡眠和不可中斷睡眠。可中斷睡眠(絕大多數情況下是可中斷睡眠)意味着盡管程序處于等待隊列,但是卻可以被信号将其喚醒。例如,發起一個阻塞的系統調用(譬如,read()終端裝置會阻塞到有資料輸入為止)會導緻程序進入可中斷睡眠狀态,核心随後通過程序排程函數schedule()排程運作别的程序,但在每次程序排程的時候,schedule()函數在傳回之前,都會檢測處于可中斷睡眠狀态的程序是否有未決的信号(記錄在該程序的PCB中),如果有,則先處理信号,之後系統調用傳回失敗,并将errno設定為EINTR。

In the ps listing above, the STAT column displays the current state of each process. The same column may also contain one or more attributes, or flags:

在上面的ps輸出結果中,STAT列顯示的是程序的目前狀态。這一列一般還會包含程序的更多屬性或标志,如下:

s This process is a session leader.

+ This process is part of a foreground process group.

These attributes are used for job control.

s 該程序是個會話首程序

+ 該程序是個前台程序(屬于前台程序組,一個會話隻有一個前台程序組)

這些屬性在作用控制中都會用到。 

Jobs and sessions 作業控制和會話管理

    Job control is what happens when you press ^Z to suspend a program, or when you start a program in the background using &. A job is the same as a process group. Internal shell commands like jobs, fg and bg can be used to manipulate the existing jobs within a session. Each session is managed by a session leader, the shell, which is cooperating tightly with the kernel using a complex protocol of signals and system calls.

    所謂作業(作業中的程序屬于同一個程序組)控制,大家平時肯定都用過。比如你按下CTRL+Z(^Z)挂起一個程式,或者使用&使一個程式在背景運作。SHELL也提供相應的作業控制指令(jobs,fg,bg)來操作一個會話中的作業。一個會話是通過會話首程序來管理的,比如SHELL程式一般就是一個會話首程序,它通過複雜的信号機制和系統調用與核心互動。

The following example illustrates the relationship between processes, jobs and sessions:

下面的例子很好的诠釋了程序,作業和會話的關系:

The following shell interactions... 下面是一系列通過shell的互動...

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

...correspond to these processes...這些互動對應一下這些程序...

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

...and these kernel structures. ...并且對應這些核心資料結構。

  • TTY Driver (/dev/pts/0).

    Size: 45x13

    Controlling process group: (101)

    Foreground process group: (103)

    UART configuration (ignored, since this is an xterm): Baud rate, parity, word length and much more.

    Line discipline configuration: cooked/raw mode, linefeed correction, meaning of interrupt characters etc.

    Line discipline state: edit buffer (currently empty), cursor position within buffer etc.

  • pipe0

    Readable end (connected to PID 104 as file descriptor 0)

    Writable end (connected to PID 103 as file descriptor 1)

    Buffer

  • TTY驅動程式(/dev/pts/0)

    大小:45x13

    控制程序組号:(101)

    前台程序組号:(103)

    UAR配置(忽略,因為這是xterm):波特率,奇偶校驗,字長等等。

    線路規程配置:規範模式輸入處理/非規範模式輸入處理,linefeed correction,中斷字元的定義等等

    線路規程狀态:編輯緩沖區(目前是否為空),光标在緩沖區中的目前位置等等

    管道

  • 可讀(作為程序104的标準輸入(檔案描述符0))

    可寫(作為程序103的标準輸出(檔案描述符1))

    緩沖區

    The basic idea is that every pipeline is a job, because every process in a pipeline should be manipulated (stopped, resumed, killed) simultaneously. That's why kill(2) allows you to send signals to entire process groups. By default, fork(2) places a newly created child process in the same process group as its parent, so that e.g. a ^C from the keyboard will affect both parent and child. But the shell, as part of its session leader duties, creates a new process group every time it launches a pipeline.

    首先需要了解到的是,通常SHELL認為組成一個管道的一系列程序(以及其衍生出的程序)是一個作業,因為一個作業中的程序可以同時被控制(停止,繼續,殺死)。知道了這點,對于kill(2)可以發送信号給一個程序組就不難了解了。此外,fork(2)預設的将建立的子程序放到與父程序同樣的程序組中,是以當我們按下^C(Ctrl+C)後就會發現父程序和子程序都消失了。SHELL通常都是一個會話的首程序,每當我們使用管道建立一個作業時,SHELL都會建立一個新的程序組,作業中的程序全部屬于這個新的程序組。

    The TTY driver keeps track of the foreground process group id, but only in a passive way. The session leader has to update this information explicitly when necessary. Similarly, the TTY driver keeps track of the size of the connected terminal, but this information has to be updated explicitly, by the terminal emulator or even by the user.

    TTY驅動程式會一直跟蹤前台程序組的ID,當然了,TTY驅動程式不會主動擷取,而是必要的時候,會話首程序去顯示地告訴TTY驅動程式。同樣的,TTY驅動程式也會一直跟蹤終端的視窗大小,不同的是,不是會話首程序告之的,而是虛拟終端(terminal emulator)或者是使用者自己。

    As you can see in the diagram above, several processes have /dev/pts/0 attached to their standard input. But only the foreground job (the ls | sort pipeline) will receive input from the TTY. Likewise, only the foreground job will be allowed to write to the TTY device (in the default configuration). If the cat process were to attempt to write to the TTY, the kernel would suspend it using a signal.

    觀察上面的圖表,我們會發現,大部分程序的标準輸入(standard input)是/dev/pts/0。但是事實上,隻有前台作業(指的是ls | sort pipeline)可以讀取終端上的資料。同樣的,也隻有前台作業允許往終端寫入資料(當然這一切成立的前提是遵循系統預設的配置)。是以我們可以看到,當cat程式試圖讀取(原文是wirte,我個人覺得不準确,因為cat程式首先讀取終端,然後才寫終端)終端資料時,核心發送SIGTTIN信号将其挂起。

Signal madness 瘋狂的信号

Now let's take a closer look at how the TTY drivers, the line disciplines and the UART drivers in the kernel communicate with the userland processes.

現在讓我們進一步了解核心中的TTY驅動程式,線路規程以及UART驅動程式是如何與使用者空間的應用程式交流通信的。

UNIX files, including the TTY device file, can of course be read from and written to, and further manipulated by means of the magic ioctl(2) call (the Swiss army-knife of UNIX) for which lots of TTY related operations have been defined. Still, ioctl requests have to be initiated from processes, so they can't be used when the kernel needs to communicate asynchronously with an application.

我們都知道,“一切皆檔案”是UNIX的基本哲學之一,這裡的檔案當然也包括TTY裝置檔案,應用程式都可以讀取和寫入資料,更多的操作可以通過神奇的ioctl(2)系統調用來完成(ioctl是UNIX中的瑞士軍刀),它定義了很多與TTY相關的操作。但是可惜的是,ioctl依然是應用程式調用的接口,是以核心不可能利用它來異步地同應用程式交流通信。

In The Hitchhiker's Guide to the Galaxy, Douglas Adams mentions an extremely dull planet, inhabited by a bunch of depressed humans and a certain breed of animals with sharp teeth which communicate with the humans by biting them very hard in the thighs. This is strikingly similar to UNIX, in which the kernel communicates with processes by sending paralyzing or deadly signals to them. Processes may intercept some of the signals, and try to adapt to the situation, but most of them don't.

在電影《銀河系漫遊指南》中,導演Douglas Adams提到過一個非常特别的星球,星球上居住着一群沮喪的人,并且該星球上還有一種長着鋒利牙齒的動物,這些動物是通過緊緊的咬人的大腿而與人交流的。這種交流方式和UNIX核心與應用程式通信的方式很相像,核心通過發送信号的方式與應用程式通信,應用程式接收到信号後,也許會做出相應的處理,也許會直接退出或者挂起進入休眠狀态。

So a signal is a crude mechanism that allows the kernel to communicate asynchronously with a process. Signals in UNIX aren't clean or general; rather, each signal is unique, and must be studied individually.

信号用來通知程序發生了異步事件。每一個信号在核心中都是獨一無二的,我們必須搞清楚每一個信号的功能。

You can use the command kill -l to see which signals your system implements. This is what it may look like:

你可以使用指令kill -l來檢視你的系統中實作了哪些信号。就像這樣:

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

As you can see, signals are numbered starting with 1. However, when they are used in bitmasks (e.g. in the output of ps s), the least significant bit corresponds to signal 1.

可以發現,信号編号是從1開始的,但是在信号屏蔽字表示方法中,最低有效為對應信号1。

This article will focus on the following signals: SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGCHLD, SIGSTOP, SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU and SIGWINCH.

在本文中,我們主要關注以下幾個信号:SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGCHLD, SIGSTOP, SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU 和 SIGWINCH.

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

SIGHUP

預設動作:終止程序

可能的動作:終止程序,忽略該信号,調用相應的信号處理函數

如果終端接口檢測到一個連接配接斷開時(數據機或網絡的斷開),UART驅動程式就會發送SIGHUP信号給與該終端相關的控制程序(會話首程序)。通常,該信号會殺死所有的程序,不過有些程式,例如nohup(1)和screen(1),因為與它們的會話分離(控制終端),是以它們的子程序并不會接收到SIGHUP信号。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

  根據POSIX.1定義:

  • 挂斷信号(SIGHUP)預設的動作是終止程式。
  • 當終端接口檢測到網絡連接配接斷開,将挂斷信号發送給控制程序(會話期首程序)。
  • 如果會話期首程序終止,則該信号發送到該會話期前台程序組。
  • 一個程序退出導緻一個孤兒程序組中産生時,如果任意一個孤兒程序組程序處于STOP狀态,發送SIGHUP和SIGCONT信号到該程序組中所有程序。

  是以當網絡斷開或終端視窗關閉後,控制程序收到SIGHUP信号退出,會導緻該會話期内其他程序退出。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:
TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:
TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:
TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:
TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

An example 一個執行個體

Suppose that you are editing a file in your (terminal based) editor of choice. The cursor is somewhere in the middle of the screen, and the editor is busy executing some processor intensive task, such as a search and replace operation on a large file. Now you press ^Z. Since the line discipline has been configured to intercept this character (^Z is a single byte, with ASCII code 26), you don't have to wait for the editor to complete its task and start reading from the TTY device. Instead, the line discipline subsystem instantly sends SIGTSTP to the foreground process group. This process group contains the editor, as well as any child processes created by it.

假設現在你正在使用你最喜歡的編輯器(基于終端的)編輯一個檔案,光标停留在螢幕中央,而此時編輯器正在運作一個很耗CPU的任務,比如在一個很大的檔案中查找關鍵字,或者替換操作。現在你按了下^Z(Ctrl+Z),因為線路規程預設設定(規範模式輸入處理)是會截取這個特殊字元的(注意:^Z是一個字元,ASCII碼是26),是以你不必等待編輯器執行完目前的任務然後去讀取TTY裝置,相反的,線路規程會立刻發送SIGTSTP信号給前台程序組,立刻停止前台程序組中的所有程序,我們的編輯器屬于前台程序中程序,當然也包括編輯器的子程序,是以我們的編輯器會立刻挂起停止。

The editor has installed a signal handler for SIGTSTP, so the kernel diverts the process into executing the signal handler code. This code moves the cursor to the last line on the screen, by writing the corresponding control sequences to the TTY device. Since the editor is still in the foreground, the control sequences are transmitted as requested. But then the editor sends a SIGSTOP to its own process group.

那麼在編輯器接收到SIGTSTP信号到停止這一過程中做了什麼呢?一般編輯器在程式中會設定一個信号SIGTSTP的信号處理函數,是以當程式接收到SIGTSTP信号後,核心會轉而去運作這個信号處理函數 -- 函數中,會通過向TTY裝置寫入相應的控制指令來使光标移動到螢幕的最後一行。因為此時編輯器還是前台作業,是以這個控制指令會如實寫入TTY裝置,接着編輯器發送信号SIGSTOP給自己所在的程序組,到此編輯器及其相關子程序才算真正停止。

The editor has now been stopped. This fact is reported to the session leader using a SIGCHLD signal, which includes the id of the suspended process. When all processes in the foreground job have been suspended, the session leader reads the current configuration from the TTY device, and stores it for later retrieval. The session leader goes on to install itself as the current foreground process group for the TTY using an ioctl call. Then, it prints something like "[1]+ Stopped" to inform the user that a job was just suspended.

編輯器現在處于停止狀态。作業系統檢測到編輯器狀态的改變,進而發送SIGCHLD信号給會話首程序SHELL(會話首程序在這裡是編輯器程序的父程序),報告其子程序的資訊,這其中就包括被挂起程序的程序ID。目前台程序組中的所有程序都停止運作時,會話首程序SHELL會讀取目前TTY裝置的配置資訊,以備後面恢複作業時用到。接着會話首程序SHELL通過ioctl系統調用使自己成為目前前台程序組(SHELL的提示符會出現)。接着列印出“[1]+ Stopped”資訊顯示地告訴使用者一個作業被挂起了。

At this point, ps(1) will tell you that the editor process is in the stopped state ("T"). If we try to wake it up, either by using the bg built-in shell command, or by using kill(1) to send SIGCONT to the process(es), the editor will start executing its SIGCONT signal handler. This signal handler will probably attempt to redraw the editor GUI by writing to the TTY device. But since the editor is now a background job, the TTY device will not allow it. Instead, the TTY will send SIGTTOU to the editor, stopping it again. This fact will be communicated to the session leader using SIGCHLD, and the shell will once again write "[1]+ Stopped" to the terminal.

此時,通過ps(1)檢視可以看到編輯器的狀态是停止狀态(“T”)。如果你想喚醒編輯器,那麼可以使用SHELL内建的指令bg,或者直接通過kill(1)發送SIGCONT信号給編輯器程序,編輯器程序會被喚醒,重新開始從它的SIGCONT信号處理函數中運作。在信号函數中,編輯器也許會試圖通過寫TTY裝置來重繪畫面,但是别忘了,因為編輯器程序此時是個背景程序,而背景程序是不允許往控制終端(TTY裝置)寫入任何資料的,是以終端驅動程式會發送信号SIGTTOU信号給編輯器,該信号會暫時停止編輯器程序。同樣的,編輯器程序狀态的改變會引起核心發送SIGCHLD信号給會話首程序,是以SHELL會再次輸出“[1]+ Stopped”資訊到終端上。

When we type fg, however, the shell first restores the line discipline configuration that was saved earlier. It informs the TTY driver that the editor job should be treated as the foreground job from now on. And finally, it sends a SIGCONT signal to the process group. The editor process attempts to redraw its GUI, and this time it will not be interrupted by SIGTTOU since it is now a part of the foreground job.

當我們輸入fg指令後,SHELL首先會恢複之前儲存的TTY裝置資訊,通知TTY裝置從現在開始編輯器程序是前台作業了。接着,SHELL發送信号SIGCONT給編輯器程序所在的程序組。接收信号後,編輯器程序試圖重繪畫面,而這次就不會被信号SIGTTOU阻止了,因為它已經是前台作業中的一個程序了。

Flow control and blocking I/O 流控制 和 阻塞的I/O

Run yes in an xterm, and you will see a lot of "y" lines swooshing past your eyes. Naturally, the yes process is able to generate "y" lines much faster than the xterm application is able to parse them, update its frame buffer, communicate with the X server in order to scroll the window and so on. How is it possible for these programs to cooperate?

當我們在xtern上運作yes指令時,會看到一連串的“y”嗖的一聲在我們眼前劃過。但是我們知道,xterm一般需要先讀取這些資料,然後更新自己的幀緩沖區,最後需要與圖形服務通信做出相應的操作,例如翻頁操作等等,但是這一系列過程的速度肯定是低于yes程式往其寫的速度的,那麼它們是如何配合而完成這一切操作的呢?

The answer lies in blocking I/O. The pseudo terminal can only keep a certain amount of data inside its kernel buffer, and when that buffer is full and yes tries to call write(2), then write(2) will block, moving the yes process into the interruptible sleep state where it remains until the xterm process has had a chance to read off some of the buffered bytes.

答案就是依靠阻塞的I/O。僞終端一般隻會在核心緩沖區中保留固定大小的資料,當資料滿時,核心就會調用系統調用wirte(2),由于要一次性寫完這麼多資料,是以write(2)會阻塞在那裡,與此同時,yes程序也被核心設定為睡眠狀态(可中斷睡眠狀态)。這種狀态會一直持續到xterm程序已經可以讀取緩沖區中的資料了為止。

The same thing happens if the TTY is connected to a serial port. yes would be able to transmit data at a much higher rate than, say, 9600 baud, but if the serial port is limited to that speed, the kernel buffer soon fills up and any subsequent write(2) calls block the process (or fail with the error code EAGAIN if the process has requested non-blocking I/O).

當TTY與一個序列槽相連時,道理也是一樣的。比如這個序列槽的最大波特率是9600波特,但是你向其寫入資料的速度可能會遠遠大于這個速度,那麼核心緩沖區會很快被填滿,是以後續的系統調用write(2)會阻塞我們的程式(如果我們程式中設定的是非阻塞I/O模式的話,那麼write會直接傳回錯誤碼EAGAIN而傳回),以保證資料正常的顯示在序列槽上。

What if I told you, that it is possible to explicitly put the TTY in a blocking state even though there is space left in the kernel buffer? That until further notice, every process trying to write(2) to the TTY automatically blocks. What would be the use of such a feature?

如果我現在告訴你,盡管有時候核心緩沖區沒有滿,但是也有可能顯示地設定TTY為阻塞模式的話,你會不會覺得很奇怪?也就是說,在這種情況下,任何調用write(2)的程式都會被自動阻塞。這個特殊的特性有什麼用處呢?

Suppose we're talking to some old VT-100 hardware at 9600 baud. We just sent a complex control sequence asking the terminal to scroll the display. At this point, the terminal gets so bogged down with the scrolling operation, that it's unable to receive new data at the full rate of 9600 baud. Well, physically, the terminal UART still runs at 9600 baud, but there won't be enough buffer space in the terminal to keep a backlog of received characters. This is when it would be a good time to put the TTY in a blocking state. But how do we do that from the terminal?

假設我們正在使用一個波特率為9600的老式VT-100終端裝置上工作,并且發送了一個複雜的控制指令給它,讓它執行翻頁操作,我們會發現終端會處于接近“崩潰”狀态非常得卡頓,那麼這個時候終端是不可能在波特率為9600的情況下接收新的資料的(此時終端的速度應該低于9600)。然而,UART驅動程式此時的速率依然是9600,那麼終端的緩沖區(buffer space in the terminal)很快就會滿,根本沒有足夠的空間來存儲大量的資料,是以如果這個時候能将TTY設定為阻塞狀态的話,不就解決了這一難題了嗎? 現在的問題就是如何設定的問題了!

We have already seen that a TTY device may be configured to give certain data bytes a special treatment. In the default configuration, for instance, a received ^C byte won't be handed off to the application through read(2), but will instead cause a SIGINT to be delivered to the foreground job. In a similar way, it is possible to configure the TTY to react on a stop flow byte and a start flow byte. These are typically ^S (ASCII code 19) and ^Q (ASCII code 17) respectively. Old hardware terminals transmit these bytes automatically, and expect the operating system to regulate its flow of data accordingly. This is called flow control, and it's the reason why your xterm sometimes appears to lock up when you accidentally press ^S.

我們已經了解了TTY裝置可以設定成規範模式,在這種模式下會對于特殊的輸入字元做特殊的處理。舉例來說,TTY接收到^C(Ctrl+C)時,并不是将該字元直接傳遞給應用程式(應用程式通過read(2)來讀取),而是發送一個SIGINT信号給前台作業。同樣的,可以發送特殊的字元給TTY裝置,使其可以停止接收位元組流或者開始接收位元組流。實際上,一般是通過特殊字元^S(Ctrl+S)和^Q(Ctrl+Q)來分别實作這兩種功能的。老式的終端裝置直接将這兩個字元傳遞給作業系統,讓作業系統來相應的控制位元組流。這就是流控制的概念,并且這也就能夠解釋當你無意按了^S(Ctrl+S)後,為什麼你的xterm好像被鎖住了一樣的原因了。

There's an important difference here: Writing to a TTY which is stopped due to flow control, or due to lack of kernel buffer space, will block your process, whereas writing to a TTY from a background job will cause a SIGTTOU to suspend the entire process group. I don't know why the designers of UNIX had to go all the way to invent SIGTTOU and SIGTTIN instead of relying on blocking I/O, but my best guess is that the TTY driver, being in charge of job control, was designed to monitor and manipulate whole jobs; never the individual processes within them.

這裡需要我們弄清楚的是:由于流控制而把TTY設定成阻塞模式導緻的程序阻塞,和由于核心緩沖區已經滿了而導緻的程序阻塞是不一樣的。當背景作業試圖往終端寫資料時,終端驅動程式會發送信号SIGTTOU給程序而使其停止運作。我一直不明白為什麼UNIX的設計者一緻同意使用信号SIGTTOU和SITTIN而不采用阻塞的I/O來停止程序。我想這其中的原因也許是因為終端驅動程式管理者作業控制,它監視和操作的是整個作業,而絕不是作業中的某一單個程序吧。

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

Configuring the TTY device 配置TTY裝置

To find out what the controlling TTY for your shell is called, you could refer to the ps l listing as described earlier, or you could simply run the tty(1) command.

想知道你的SHELL的控制終端(controlling TTY)是什麼,你可以通過之前的ps l指令來檢視,或者直接通過tty(1)來獲得。

A process may read or modify the configuration of an open TTY device using ioctl(2). The API is described in tty_ioctl(4). Since it's part of the binary interface between Linux applications and the kernel, it will remain stable across Linux versions. However, the interface is non-portable, and applications should rather use the POSIX wrappers described in the termios(3) man page.

我們可以通過ioctl來讀取或者設定修改一個TTY裝置的配置屬性。相關的API可以參看tty_ioctl(4)。因為,這是應用程式和核心的二進制接口的一部分,是以他們在不同的linux版本中一直保持穩定不變。但他們是不可移植的,是以推薦應用程式調用POSIX封裝的接口(參考termios(3)man文檔)。

I won't go into the details of the termios(3) interface, but if you're writing a C program and would like to intercept ^C before it becomes a SIGINT, disable line editing or character echoing, change the baud rate of a serial port, turn off flow control etc. then you will find what you need in the aforementioned man page.

在這裡,我不打算詳談termios(3)接口,但如果你是C程式員,你想在^C(Ctrl+C)被換成信号SIGINT之前截取該字元,又或者你想關閉行編輯功能,關閉回顯功能,改變序列槽的波特率,關閉流控制等等的話,那麼你就的仔細閱讀termios(3)man文檔了。

There is also a command line tool, called stty(1), to manipulate TTY devices. It uses the termios(3) API.

系統同樣提供了stty(1)指令來讓我們操作TTY裝置,它就是用termios(3)API實作的。

Let's try it!

讓我們試一試吧!

TTY解密(The TTY demystified)TTY解密(The TTY demystified)參考連結:

The -a flag tells stty to display all settings. By default, it will look at the TTY device attached to your shell, but you can specify another device with -F.

參數-a說明羅列出所有的設定。預設的,stty為呈現目前SHELL的控制終端的的屬性設定,但是你也可以通過-F參數指定其他的終端裝置。

Some of these settings refer to UART parameters, some affect the line discipline and some are for job control. All mixed up in a bucket for monsieur. Let's have a look at the first line:

其中的有些設定屬性是與UART相關的,有的與線路規程相關,有的與作業控制相關。這些屬性全部糅合在一起了。我們來看第一行:

Attribute Related part Description
speed   UART          The baud rate. Ignored for pseudo terminals.
rows,columns                        TTY driver Somebody's idea of the size, in characters, of the terminal attached to this TTY device. Basically, it's just a pair of variables within kernel space, that you may freely set and get. Setting them will cause the TTY driver to dispatch a SIGWINCH to the foreground job.
line Line discipline                 The line discipline attached to the TTY device. 0 is N_TTY. Listed in /proc/tty/ldiscs.

Try the following: Start an xterm. Make a note of its TTY device (as reported by tty) and its size (as reported by stty -a). Start vim (or some other full-screen terminal application) in the xterm. The editor queries the TTY device for the current terminal size in order to fill the entire window. Now, from a different shell window, type:

我們可以做一個實驗:開啟一個xterm,獲得它的終端裝置的名稱(通過tty(1)程式獲得)和視窗大小(通過stty -a獲得),然後運作vim程式(也可以是其他預設使用全屏的終端應用程式)。vim編輯器首先會擷取終端裝置的目前視窗大小屬性,并依此填充整個視窗。現在我們從另一個SHELL視窗,運作如下指令:

stty -F X rows Y

where X is the TTY device, and Y is half the terminal height. This will update the TTY data structure in kernel memory, and send a SIGWINCH to the editor, which will promptly redraw itself using only the upper half of the available window area.

X是你的終端名稱,Y是你目前終端高度的一半大小數值。這會更新核心記憶體中的與終端裝置相關的資料結構,并且核心會給編輯器發送SGIWINCH信号,那麼vim會迅速的使用這新新值來重繪視窗區域。

The second line of stty -a output lists all the special characters. Start a new xterm and try this:

從stty -a輸出的第二行我們看到羅列的是系統定義的特殊字元。開啟一個新的xterm,并輸入以下指令:

stty intr o

Now "o", rather than ^C, will send a SIGINT to the foreground job. Try starting something, such as cat, and verify that you can't kill it using ^C. Then, try typing "hello" into it.

現在你會發現,發送信号SIGINT給前台作業的是字元“o”了,而不再是^C(Ctrl+C)。接着運作cat程式,你會發現^C已經不能殺死程序了,相反的,當我們試圖駛入“hello”字元串是,遇到“o”cat就退出了。

Occasionally, you may come across a UNIX system where the backspace key doesn't work. This happens when the terminal emulator transmits a backspace code (either ASCII 8 or ASCII 127) which doesn't match the erase setting in the TTY device. To remedy the problem, one usually types stty erase ^H (for ASCII 8) or stty erase ^? (for ASCII 127). But please remember that many terminal applications use readline, which puts the line discipline in raw mode. Those applications aren't affected.

也許你曾遇到過這樣的窘境,你的UNIX系統倒退鍵不起作用了,這是因為你發送的倒退鍵控制碼(ASCII編号8或者127)與TTY裝置的預設設定不一緻導緻的。修複這個問題,可以通過設定stty erase ^H (for ASCII 8)或者stty erase ^? (for ASCII 127)來修複。但是,别忘了,有很多終端應用程式使用了readline庫,而readline庫是把線路規程設定成非規範模式的,是以這些設定對這些程式根本沒有影響。

Finally, stty -a lists a bunch of switches. As expected, they are listed in no particular order. Some of them are UART-related, some affect the line discipline behaviour, some are for flow control and some are for job control. A dash (-) indicates that the switch is off; otherwise it is on. All of the switches are explained in the stty(1) man page, so I'll just briefly mention a few:

最後,stty -a羅列了很多的功能開關。正如你見到的,它們的輸出排列是不分先後的。同樣的,它們有的與UART相關,有的與線路規程相關,有的與流控制相關,與的則與作業控制相關。破折号(-)表明該功能已被禁止,相反,沒有破折号(-)的則說明該功能已經開啟。所有功能的較長的描述可參數tty(1)的man文檔。是以這裡我簡略的介紹介個:

icanon toggles the canonical (line-based) mode. Try this in a new xterm:

icanon标志用來設定終端的模式,在一個新的xterm中運作如下指令:

stty -icanon; cat

Note how all the line editing characters, such as backspace and ^U, have stopped working. Also note that cat is receiving (and consequently outputting) one character at a time, rather than one line at a time.

你會發現很多特殊字元已經沒有作用了,例如倒退鍵和^U(Ctrl+U删除整行)等。并且cat程式現在一次隻讀取一個字元(一次輸出也是一個字元),而不是以前的整行了。

echo enables character echoing, and is on by default. Re-enable canonical mode (stty icanon), and then try:

echo标志用來設定回顯功能,并且系統預設是設定回顯功能的。重新設定終端為規範模式(canonical mode)後,在輸入以下指令:

stty -echo; cat

As you type, your terminal emulator transmits information to the kernel. Usually, the kernel echoes the same information back to the terminal emulator, allowing you to see what you type. Without character echoing, you can't see what you type, but we're in cooked mode so the line editing facilities are still working. Once you press enter, the line discipline will transmit the edit buffer to cat, which will reveal what your wrote.

當你輸入字元的時候,你的虛拟終端會将這些資訊傳遞給核心。通常,核心會回顯這些資訊在你的虛拟終端上,讓你實時地看見你輸入的資訊。但是沒了回顯功能,你就不能看到這些資訊了,可是因為我們處在規範模式下,是以行編輯功能依然有效,所有一旦你回車,線路規程就會将編輯緩沖區中的資訊傳遞給cat程式,這時我們就能看到我們輸入的是什麼了。

tostop controls whether background jobs are allowed to write to the terminal. First try this:

tostop标志用來控制背景作業是否可以往終端寫入資料。我們的實驗如下,先輸入這些:

stty tostop; (sleep 5; echo hello, world) &

The & causes the command to run as a background job. After five seconds, the job will attempt to write to the TTY. The TTY driver will suspend it using SIGTTOU, and your shell will probably report this fact, either immediately, or when it's about to issue a new prompt to you. Now kill the background job, and try the following instead:

符号&使這個指令作為後端作業。5秒後,這個作業試圖往終端寫入資料,然後TTY驅動程式會發送信号SIGTTOU給作業并挂起作業,你的SHELL也許會向你報告這個事實(我的bash下是鍵入回車後才會提示我),也許隻是迅速的列印出一個新的指令提示符給你。現在kill掉這個背景作業,并運作下面的指令:

stty -tostop; (sleep 5; echo hello, world) &

You will get your prompt back, but after five seconds, the background job transmits hello, world to the terminal, in the middle of whatever you were typing.

運作的結果是,你會立刻看到SHELL列印出指令提示符。但是5秒後,不管你正在輸入什麼,這個背景作業都會在終端上列印出“hello, world”字元串。

Finally, stty sane will restore your TTY device configuration to something reasonable.

最後,通過stty sane指令可以恢複TTY裝置的所有預設配置。

Conclusion 大總結

I hope this article has provided you with enough information to get to terms with TTY drivers and line disciplines, and how they are related to terminals, line editing and job control. Further details can be found in the various man pages I've mentioned, as well as in the glibc manual (info libc, "Job Control").

我希望本文可以讓你很好的了解TTY驅動程式和線路規程的作用以及它們與終端,行編輯和作業控制的關系。更詳細的讨論讀者可以參看本文推薦的相關man文檔,和glibc官方文檔(info libc, "Job Control")。

Finally, while I don't have enough time to answer all the questions I get, I do welcome feedback on this and other pages on the site. Thanks for reading!

最後,盡管我沒有足夠的時間來回答提出的所有問題,但是我會盡量回複你們,謝謝閱讀!

參考連結:

《徹底了解Linux的各種終端類型以及概念》

《Linux終端和Line discipline圖解》

《Terminals》《CORE TECHNOLOGY: SIGNALS》《TTY》

《你真的知道什麼是終端嗎?》

《Linux運作與控制背景程序的方法:nohup, setsid, &, disown, screen》

《 對于Linux核心tty裝置的一點了解 》

《TTY的那些事兒》

《程序管理和終端驅動:基本概念》

《Linux TTY framework》

《Linux Virtual Console》

《程序管理和終端驅動:基本概念》

《Why the TTY line discipline exists in the kernel》

《[術語] 什 line discipline (線路規程)》

《Linux Serial Console HOWTO》

《Understanding UNIX termios VMIN and VTIME》

《tty裝置驅動程式設計》

《再談UNIX流機制和tty驅動》

《A Brief Introduction to Termios》

《Using pseudo-terminals (pty) to control interactive programs》

《how to open, read, and write from serial port in C》

《Linux的控制台(TTY/PTY)與多任務(MULTI-TASKING》

《 線路規程(line disciplines)》

《Linux終端(一)》

《Linux TTY/PTS概述》《Linux session和程序組概述》

《Linux 的僞終端的基本原理 及其在遠端登入(SSH,telnet等)中的應用》

《什麼是 Terminal》

《What are the responsibilities of each Pseudo-Terminal (PTY) component (software, master side, slave side)?》

《linux的終端,網絡虛拟終端,僞終端 》

《第 34 章 終端、作業控制與守護程序》

《what is the difference between tty and vty in linux》

《Linux 僞終端的基本原理 及其在遠端登入(SSH,telnet等)中的應用》(https://www.cnblogs.com/zzdyyy/p/7538077.html)

《終端tty、虛拟控制台、FrameBuffer的切換過程詳解》

《終端的前世今生》

《In UNIX programming, what is the "controlling terminal"? What is the intuition behind having background and foreground process groups?》

《Why does reading from two connected pty's cause an infinite loop?》《解密TTY》

《Console-terminal-tty-shell-kernel》

《Linux 程序組(process group)和會話(session)》

《[리눅스커널][시그널] 시그널 생성: 커널은 언제 시그널 생성할까?》

《指令行界面 (CLI)、終端 (Terminal)、Shell、TTY,傻傻分不清楚?》

《特集 : 作ってみよう,MyガジェットPart3》

《How does Ctrl-C terminate a child process?》

《Virtual creatures and their habitats: the past and present TTY in Linux》

《Upgrading simple shells to fully interactive TTYs》《suspended(tty input)》

《Get rid of SIGINT》

繼續閱讀