
在 Lubuntu 18.04 上,我在 lxterminal 中執行 shell。它的控制終端是目前的偽終端從機:
$ tty
/dev/pts/2
我想知道我目前的控制終端/dev/pts/2
和/dev/tty
.
/dev/tty
就像我目前的控制終端一樣/dev/pts/2
:$ echo hello > /dev/tty hello $ cat < /dev/tty world world ^C
但它們似乎是不相關的文件,而不是一個到另一個的符號連結或硬連結:
$ ls -lai /dev/tty /dev/pts/2 5 crw--w---- 1 t tty 136, 2 May 31 16:38 /dev/pts/2 13 crw-rw-rw- 1 root tty 5, 0 May 31 16:36 /dev/tty
對於具有不同控制終端的不同會話,
/dev/tty
保證if是它們的控制終端。它怎麼可能是不同的控制終端,而不是符號連結或硬連結?
那麼他們有什麼聯繫和區別呢?任何幫助深表感謝!
這篇文章源自於先前的一篇文章指令 `tty` 和檔案 `/dev/tty` 的輸出都指向目前 bash 進程的控制終端嗎?
答案1
tty
第 4 節中的線上說明頁聲稱如下:
文件/dev/tty是字元文件,主裝置號碼為 5,次裝置號碼為 0,通常模式為 0666,擁有者為 root.tty。它是進程的控制終端(如果有)的同義詞。
除了
ioctl(2)
tty 所指的設備支援的請求ioctl(2)
TIOCNOTTY
支援請求。
TIOCNOTTY
將調用進程與其控制終端分離。
如果該進程是會話領導者,
SIGHUP
則SIGCONT
訊號將傳送到前台進程組,並且當前會話中的所有進程都會失去其控制 tty。這
ioctl(2)
呼叫僅適用於連接到的檔案描述符 /dev/tty。當使用者在終端呼叫守護程序時,它由守護程序使用。該進程嘗試開啟/dev/tty。如果開啟成功,則使用 將自身與終端分離TIOCNOTTY
,而如果開啟失敗,則顯然它沒有附加到終端,不需要自行分離。
這可以部分解釋為什麼/dev/tty
不是控制終端的符號連結:它將支援額外的ioctl
,並且可能沒有控制終端(但進程總是可以嘗試存取/dev/tty
)。但是文檔不正確:附加內容ioctl
不僅可以訪問透過 /dev/tty
(看莫斯維的回答,這也對 ) 的性質給出了更合理的解釋/dev/tty
。
/dev/tty
可以代表不同的控制終端,而不是鏈接,因為實現它的驅動程式確定調用進程的控制終端是什麼(如果有)。
您可以將其視為/dev/tty
控制終端,從而提供僅對控制終端有意義的功能,而/dev/pts/2
etc. 是普通終端,其中之一可能恰好是給定進程的控制終端。
答案2
/dev/tty
是一個「神奇」的字元設備,打開時會返回目前終端的句柄。
假設控制終端是/dev/pts/1
,則透過via開啟的檔案描述符/dev/pts/1
和透過via開啟的檔案描述符/dev/tty
將引用同一個設備;任何寫入、讀取或其他檔案操作在它們中的任何一個上都會以相同的方式進行。
特別是,它們將接受相同的 ioctl 集,並且TIOCNOTTY
不是只能透過以下方式獲得的額外 ioctl/dev/tty
。
ioctl(fd, TIOCNOTTY)
在引用終端的任何檔案描述符上的工作方式都是相同的,前提是它是呼叫它的程序的控制終端。
/dev/tty
描述符是否是透過打開, /dev/pts/1
,獲得的並不重要/dev/ptmx
(在這種情況下,ioctl 將作用於其相應的奴隸),或者最近,透過調用ioctl(master, TIOCGPTPEER, flags)
.
例子:
$ cat <<'EOT' >tiocnotty.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <err.h>
int main(int ac, char **av){
if(ioctl(0, TIOCNOTTY)) err(1, "io(TIOCNOTTY)");
if(ac < 2) return 0;
execvp(av[1], av + 1);
err(1, "execvp %s", av[1]);
}
EOT
$ cc -W -Wall tiocnotty.c -o tiocnotty
$ ./tiocnotty
$ ./tiocnotty </dev/tty
$ tty
/dev/pts/0
$ ./tiocnotty </dev/pts/0
而且,它不會真正將當前進程與 tty「分離」;該進程仍然能夠從中讀取數據,^C
終端上的 a 將殺死它,等等。 它對不是會話領導者的進程的唯一影響是 tty 將不再可通過 訪問/dev/tty
,並且將不再是列為控制 tty /proc/PID/stat
:
$ ./tiocnotty cat
^C
$ ./tiocnotty cat
^Z
[2]+ Stopped ./tiocnotty cat
$ ./tiocnotty cat
foo
foo
^D
$ ./tiocnotty cat /dev/tty
cat: /dev/tty: No such device or address
$ ./tiocnotty awk '{print$7}' /proc/self/stat
0
[第7個字段/proc/<pid>/stat
是控制tty的設備id,參見proc(5)
]
如果呼叫它的進程是會話領導者,它實際上會將會話與 tty 分離,並從會話中向前台進程組發送一個SIGHUP
/對。SIGCONT
但即便如此,終端也會不是被關閉,並且該進程仍然能夠從中讀取,如果它在以下情況下倖存下來SIGHUP
:
$ script /dev/null -c 'trap "" HUP; exec ./tiocnotty cat'
Script started, file is /dev/null
lol
lol
^C^C^C^C^C # no controlling tty anymore
wtf
wtf
^D # but still reading fine from it
Script done, file is /dev/null
/dev/tty
不是像/dev/stdin
=> /dev/fd/0
=> /proc/self/fd/0
=>這樣的符號鏈接/dev/pts/0
,因為它的發明早於像 procfs 這樣的虛擬動態檔案系統(並且通常早於符號鏈接)。許多程式已經開始依賴其特定的語義(例如,當控制終端不可存取時/dev/tty
失敗)。ENODEV