自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

當(dāng)你在終端上按下一個鍵時會發(fā)生什么?

系統(tǒng) Linux
在上個星期,我使用 xterm.js 在瀏覽器中顯示了一個交互式終端,我終于想到要問一個相當(dāng)基本的問題:當(dāng)你在終端中按下鍵盤上的一個鍵(比如 Delete,或 Escape,或 a),發(fā)送了哪些字節(jié)?

我對終端Terminal是怎么回事困惑了很久。

但在上個星期,我使用 ??xterm.js??? 在瀏覽器中顯示了一個交互式終端,我終于想到要問一個相當(dāng)基本的問題:當(dāng)你在終端中按下鍵盤上的一個鍵(比如 ??Delete???,或 ??Escape???,或 ??a??),發(fā)送了哪些字節(jié)?

像往常一樣,我們將通過做一些實驗來回答這個問題,看看會發(fā)生什么 : )

遠(yuǎn)程終端是非常古老的技術(shù)

首先,我想說的是,用 ??xterm.js?? 在瀏覽器中顯示一個終端可能看起來像一個新事物,但它真的不是。在 70 年代,計算機很昂貴。因此,一個機構(gòu)的許多員工會共用一臺電腦,每個人都可以有自己的 “終端” 來連接該電腦。

例如,這里有一張 70 年代或 80 年代的 VT100 終端的照片。這看起來像是一臺計算機(它有點大!),但它不是 —— 它只是顯示實際計算機發(fā)送的任何信息。

DEC VT100終端

DEC VT100終端

當(dāng)然,在 70 年代,他們并沒有使用 Websocket 來做這個,但來回發(fā)送的信息的方式和當(dāng)時差不多。

(照片中的終端是來自西雅圖的 ??活電腦博物館???Living Computer Museum,我曾經(jīng)去過那里,并在一個非常老的 Unix 系統(tǒng)上用 ??ed?? 編寫了 FizzBuzz,所以我有可能真的用過那臺機器或它的一個兄弟姐妹!我真的希望活電腦博物館能再次開放,能玩到老式電腦是非??岬摹#?/p>

發(fā)送了什么信息?

很明顯,如果你想連接到一個遠(yuǎn)程計算機(用 ??ssh??? 或使用 ??xterm.js?? 和 Websocket,或其他任何方式),那么需要在客戶端和服務(wù)器之間發(fā)送一些信息。

具體來說:

客戶端 需要發(fā)送用戶輸入的鍵盤信息(如 ??ls -l??)。 服務(wù)器 需要告訴客戶端在屏幕上顯示什么。

讓我們看看一個真正的程序,它在瀏覽器中運行一個遠(yuǎn)程終端,看看有哪些信息會被來回發(fā)送!

我們將使用 goterm 來進行實驗

我在 GitHub 上發(fā)現(xiàn)了這個叫做 ??goterm??? 的小程序,它運行一個 Go 服務(wù)器,可以讓你在瀏覽器中使用 ??xterm.js?? 與終端進行交互。這個程序非常不安全,但它很簡單,很適合學(xué)習(xí)。

我 ??復(fù)刻了它???,使它能與最新的 ??xterm.js?? 一起工作,因為它最后一次更新是在 6 年前。然后,我添加了一些日志語句,以打印出每次通過 WebSocket 發(fā)送/接收的字節(jié)數(shù)。

讓我們來看看在幾個不同的終端交互過程中的發(fā)送和接收情況吧!

示例:ls

首先,讓我們運行 ??ls???。下面是我在 ??xterm.js?? 終端上看到的情況:

    ~:/play$ ls
file
~:/play$

以下是發(fā)送和接收的內(nèi)容:(在我的代碼中,我記錄了每次客戶端發(fā)送的字節(jié):??sent: [bytes]???,每次它從服務(wù)器接收的字節(jié):??recv: [bytes]??)

    sent: "l"
recv: "l"
sent: "s"
recv: "s"
sent: "\r"
recv: "\r\n\x1b[?2004l\r"
recv: "file\r\n"
recv: "\x1b[~:/play$ "

我在這個輸出中注意到 3 件事:

  1. 回顯:客戶端發(fā)送??l???,然后立即收到一個??l??? 發(fā)送回來。我想這里的意思是,客戶端真的很笨 —— 它不知道當(dāng)我輸入??l??? 時,我想讓??l?? 被回顯到屏幕上。它必須由服務(wù)器進程明確地告訴它來顯示它。
  2. 換行:當(dāng)我按下回車鍵時,它發(fā)送了一個??\r'(回車)符號,而不是??\n'(換行)。
  3. 轉(zhuǎn)義序列:??\x1b??? 是 ASCII 轉(zhuǎn)義字符,所以??\x1b[?2004h?? 是告訴終端顯示什么或其他東西。我想這是一個顏色序列,但我不確定。我們稍后會詳細(xì)討論轉(zhuǎn)義序列。

好了,現(xiàn)在我們來做一些稍微復(fù)雜的事情。

示例:Ctrl+C

接下來,讓我們看看當(dāng)我們用 ??Ctrl+C?? 中斷一個進程時會發(fā)生什么。下面是我在終端中看到的情況:

    ~:/play$ cat
^C
~:/play$

而這里是客戶端發(fā)送和接收的內(nèi)容。

    sent: "c"
recv: "c"
sent: "a"
recv: "a"
sent: "t"
recv: "t"
sent: "\r"
recv: "\r\n\x1b[?2004l\r"
sent: "\x03"
recv: "^C"
recv: "\r\n"
recv: "\x1b[?2004h"
recv: "~:/play$ "

當(dāng)我按下 ??Ctrl+C??? 時,客戶端發(fā)送了 ??\x03???。如果我查 ASCII 表,??\x03??? 是 “文本結(jié)束”,這似乎很合理。我認(rèn)為這真的很酷,因為我一直對 ??Ctrl+C??? 的工作原理有點困惑 —— 很高興知道它只是在發(fā)送一個 ??\x03?? 字符。

我相信當(dāng)我們按 ??Ctrl+C??? 時,??cat??? 被中斷的原因是服務(wù)器端的 Linux 內(nèi)核收到這個 ??\x03??? 字符,識別出它意味著 “中斷”,然后發(fā)送一個 ??SIGINT?? 到擁有偽終端的進程組。所以它是在內(nèi)核而不是在用戶空間處理的。

示例:Ctrl+D

讓我們試試完全相同的事情,只是用 ??Ctrl+D??。下面是我在終端看到的情況:

    ~:/play$ cat
~:/play$

而這里是發(fā)送和接收的內(nèi)容:

    sent: "c"
recv: "c"
sent: "a"
recv: "a"
sent: "t"
recv: "t"
sent: "\r"
recv: "\r\n\x1b[?2004l\r"
sent: "\x04"
recv: "\x1b[?2004h"
recv: "~:/play$ "

它與 ??Ctrl+C??? 非常相似,只是發(fā)送 ??\x04??? 而不是 ??\x03???。很好!??\x04?? 對應(yīng)于 ASCII “傳輸結(jié)束”。

Ctrl + 其它字母呢?

接下來我開始好奇 —— 如果我發(fā)送 ??Ctrl+e??,會發(fā)送什么字節(jié)?

事實證明,這只是該字母在字母表中的編號,像這樣。

  • ??Ctrl+a?? => 1
  • ??Ctrl+b?? => 2
  • ??Ctrl+c?? => 3
  • ??Ctrl+d?? => 4
  • ...
  • ??Ctrl+z?? => 26

另外,??Ctrl+Shift+b??? 的作用與 ??Ctrl+b??? 完全相同(它寫的是??0x2??)。

鍵盤上的其他鍵呢?下面是它們的映射情況:

  • ??Tab??? -> 0x9(與??Ctrl+I?? 相同,因為 I 是第 9 個字母)
  • ??Escape??? ->??\x1b??
  • ??Backspace??? ->??\x7f??
  • ??Home??? ->??\x1b[H??
  • ??End??? ->??\x1b[F??
  • ??Print Screen??? ->??\x1b\x5b\x31\x3b\x35\x41??
  • ??Insert??? ->??\x1b\x5b\x32\x7e??
  • ??Delete??? ->??\x1b\x5b\x33\x7e??
  • 我的??Meta?? 鍵完全沒有作用

那 ??Alt??? 呢?根據(jù)我的實驗(和一些搜索),似乎 ??Alt??? 和 ??Escape??? 在字面上是一樣的,只是按 ??Alt??? 本身不會向終端發(fā)送任何字符,而按 ??Escape?? 本身會。所以:

  • ??alt + d??? =>??\x1bd??(其他每個字母都一樣)
  • ??alt + shift + d??? =>??\x1bD??(其他每個字母都一樣)
  • 諸如此類

讓我們再看一個例子!

示例:nano

下面是我運行文本編輯器 ??nano?? 時發(fā)送和接收的內(nèi)容:

    recv: "\r\x1b[~:/play$ "
sent: "n" [[]byte{0x6e}]
recv: "n"
sent: "a" [[]byte{0x61}]
recv: "a"
sent: "n" [[]byte{0x6e}]
recv: "n"
sent: "o" [[]byte{0x6f}]
recv: "o"
sent: "\r" [[]byte{0xd}]
recv: "\r\n\x1b[?2004l\r"
recv: "\x1b[?2004h"
recv: "\x1b[?1049h\x1b[22;0;0t\x1b[1;16r\x1b(B\x1b[m\x1b[4l\x1b[?7h\x1b[39;49m\x1b[?1h\x1b=\x1b[?1h\x1b=\x1b[?25l"
recv: "\x1b[39;49m\x1b(B\x1b[m\x1b[H\x1b[2J"
recv: "\x1b(B\x1b[0;7m GNU nano 6.2 \x1b[44bNew Buffer \x1b[53b \x1b[1;123H\x1b(B\x1b[m\x1b[14;38H\x1b(B\x1b[0;7m[ Welcome to nano. For basic help, type Ctrl+G. ]\x1b(B\x1b[m\r\x1b[15d\x1b(B\x1b[0;7m^G\x1b(B\x1b[m Help\x1b[15;16H\x1b(B\x1b[0;7m^O\x1b(B\x1b[m Write Out \x1b(B\x1b[0;7m^W\x1b(B\x1b[m Where Is \x1b(B\x1b[0;7m^K\x1b(B\x1b[m Cut\x1b[15;61H"

你可以看到一些來自用戶界面的文字,如 “GNU nano 6.2”,而這些 ??\x1b[27m?? 的東西是轉(zhuǎn)義序列。讓我們來談?wù)勣D(zhuǎn)義序列吧!

ANSI 轉(zhuǎn)義序列

上面這些 ??nano??? 發(fā)給客戶端的 ??\x1b[??? 東西被稱為“轉(zhuǎn)義序列”或 “轉(zhuǎn)義代碼”。這是因為它們都是以 “轉(zhuǎn)義”字符 ??\x1b??? 開頭。它們可以改變光標(biāo)的位置,使文本變成粗體或下劃線,改變顏色,等等。??維基百科介紹了一些歷史??,如果你有興趣的話可以去看看。

舉個簡單的例子:如果你在終端運行

    echo -e '\e[0;31mhi\e[0m there'

它將打印出 “hi there”,其中 “hi” 是紅色的,“there” 是黑色的。??本頁?? 有一些關(guān)于顏色和格式化的轉(zhuǎn)義代碼的例子。

我認(rèn)為有幾個不同的轉(zhuǎn)義代碼標(biāo)準(zhǔn),但我的理解是,人們在 Unix 上使用的最常見的轉(zhuǎn)義代碼集來自 VT100(博客文章頂部圖片中的那個老終端),在過去的 40 年里沒有真正改變。

轉(zhuǎn)義代碼是為什么你的終端會被搞亂的原因,如果你 ??cat??? 一些二進制數(shù)據(jù)到你的屏幕上 —— 通常你會不小心打印出一堆隨機的轉(zhuǎn)義代碼,這將搞亂你的終端 —— 如果你 ??cat??? 足夠多的二進制數(shù)據(jù)到你的終端,那里一定會有一個 ??0x1b?? 的字節(jié)。

可以手動輸入轉(zhuǎn)義序列嗎?

在前面幾節(jié)中,我們談到了 ??Home??? 鍵是如何映射到 ??\x1b[H??? 的。這 3 個字節(jié)是 ??Escape + [ + H???(因為 ??Escape??? 是??\x1b??)。

如果我在 ??xterm.js??? 終端手動鍵入 ??Escape??? ,然后是 ??[???,然后是 ??H???,我就會出現(xiàn)在行的開頭,與我按下 ??Home?? 完全一樣。

我注意到這在我的電腦上的 Fish shell 中不起作用 —— 如果我鍵入 ??Escape???,然后輸入 ??[???,它只是打印出 ??[???,而不是讓我繼續(xù)轉(zhuǎn)義序列。我問了我的朋友 Jesse,他寫過 ??一堆 Rust 終端代碼??,Jesse 告訴我,很多程序為轉(zhuǎn)義代碼實現(xiàn)了一個 超時 —— 如果你在某個最小的時間內(nèi)沒有按下另一個鍵,它就會決定它實際上不再是一個轉(zhuǎn)義代碼了。

顯然,這在 Fish shell 中可以用 ??fish_escape_delay_ms??? 來配置,所以我運行了 ??set fish_escape_delay_ms 1000??,然后我就能用手輸入轉(zhuǎn)義代碼了。工作的很好!

終端編碼有點奇怪

我想在這里暫停一下,我覺得你按下的鍵被映射到字節(jié)的方式是非常奇怪的。比如,如果我們今天從頭開始設(shè)計按鍵的編碼方式,我們可能不會把它設(shè)置成這樣:

  • ??Ctrl + a??? 和??Ctrl + Shift + a?? 做的事情完全一樣。
  • ??Alt??? 與??Escape?? 是一樣的
  • 控制序列(如顏色/移動光標(biāo))使用與??Escape??? 鍵相同的字節(jié),因此你需要依靠時間來確定它是一個控制序列還是用戶只是想按??Escape??。

但所有這些都是在 70 年代或 80 年代或什么時候設(shè)計的,然后需要永遠(yuǎn)保持不變,以便向后兼容,所以這就是我們得到的東西 :smiley:

改變窗口大小

在終端中,并不是所有你能做的事情都是通過來回發(fā)送字節(jié)發(fā)生的。例如,當(dāng)終端被調(diào)整大小時,我們必須以不同的方式告訴 Linux 窗口大小已經(jīng)改變。

下面是 ??goterm?? 中用來做這件事的 Go 代碼的樣子:

    syscall.Syscall(
syscall.SYS_IOCTL,
tty.Fd(),
syscall.TIOCSWINSZ,
uintptr(unsafe.Pointer(&resizeMessage)),
)

這是在使用 ??ioctl??? 系統(tǒng)調(diào)用。我對 ??ioctl?? 的理解是,它是一個系統(tǒng)調(diào)用,用于處理其他系統(tǒng)調(diào)用沒有涉及到的一些隨機的東西,通常與 IO 有關(guān),我猜。

??syscall.TIOCSWINSZ??? 是一個整數(shù)常數(shù),它告訴 ??ioctl?? 我們希望它在本例中做哪件事(改變終端的窗口大?。?。

這也是 xterm 的工作方式。

在這篇文章中,我們一直在討論遠(yuǎn)程終端,即客戶端和服務(wù)器在不同的計算機上。但實際上,如果你使用像 xterm 這樣的終端模擬器,所有這些工作方式都是完全一樣的,只是很難注意到,因為這些字節(jié)并不是通過網(wǎng)絡(luò)連接發(fā)送的。

文章到此結(jié)束啦

關(guān)于終端,肯定還有很多東西要了解(我們可以討論更多關(guān)于顏色,或者原始與熟化模式,或者 Unicode 支持,或者 Linux 偽終端界面),但我將在這里停止,因為現(xiàn)在是晚上 10 點,這篇文章有點長,而且我認(rèn)為我的大腦今天無法處理更多關(guān)于終端的新信息。

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2018-01-19 12:56:19

Linux進程

2023-12-13 17:04:51

終端命令shell

2015-09-25 10:41:48

r語言

2019-09-03 14:15:05

2019-02-27 10:18:26

重置Windows 10Windows

2024-01-17 11:07:09

單模光纖OS2 光纖數(shù)據(jù)中心

2015-10-29 09:35:12

BAT趨勢數(shù)據(jù)

2024-04-02 11:31:33

USBAndroid

2016-04-08 15:13:29

人工智能阿里小Ai

2014-06-17 10:57:09

2019-03-19 19:19:19

Facebook微信轉(zhuǎn)型

2013-03-15 09:57:00

虛擬化 數(shù)據(jù)中心

2015-07-28 11:22:30

大數(shù)據(jù)浪潮

2009-03-28 09:22:12

MID移動OS

2022-02-13 15:49:15

WebAssemblKubernetes容器

2015-08-03 14:06:44

2013-01-17 10:09:50

JavaSpring

2021-08-11 18:23:08

數(shù)據(jù)平臺IT

2011-03-17 15:01:11

Oracle

2015-10-19 17:50:43

云計算IT行業(yè)DevOps
點贊
收藏

51CTO技術(shù)棧公眾號