n卡控制面板(n卡控制面板最佳设置)

我对终端困惑了很久。但是上周我用xterm.js在浏览器里显示了一个交互终端。我终于想到问一个相当基本的问题:当你在终端中按下键盘上的一个键(比如Delete,

当你在终端上按下一个键时会发生什么?我对终端困惑了很久。

但是上周我用xterm.js在浏览器里显示了一个交互终端。我终于想到问一个相当基本的问题:当你在终端中按下键盘上的一个键(比如Delete,或者Escape,或者a)时,发送的是哪些字节?

像往常一样,我们会通过做一些实验来回答这个问题,看看会发生什么:)

远程终端是一项非常古老的技术。

首先我想说,用xterm.js在浏览器中显示终端,看起来可能是一件新鲜事,其实真的不是。在20世纪70年代,电脑非常昂贵。所以一个机构的很多员工会共用一台电脑,每个人都可以有自己的“终端”连接电脑。

例如,这里有一张VT100终端在70年代或80年代的照片。看起来像电脑(有点大!),但它不是——它只是显示实际计算机发送的任何信息。

EC_VT100_terminal.jpg " >

当你在终端上按下一个键时会发生什么?当然,在70年代,他们并没有使用Websocket来做到这一点,但是来回发送信息的方式和当时差不多。

(照片中的终端是来自西雅图的活体计算机博物馆。我去过那里,和ed一起在一个非常老的Unix系统上编写FizzBuzz,所以有可能我实际上使用过那台机器或者它的一个兄弟姐妹!真希望活体电脑博物馆再开一次。玩老式电脑很爽。)

发了什么信息?

显然,如果您想要连接到远程计算机(使用ssh或xterm.js和Websocket,或任何其他方式),那么您需要在客户端和服务器之间发送一些信息。

具体来说:

客户端需要发送用户输入的键盘信息(比如ls -l)。服务器需要告诉客户端在屏幕上显示什么。

我们来看一个在浏览器中运行远程终端的真实程序,看看来回会发送什么信息!

我们将使用goterm进行实验。

我在GitHub上发现了这个叫goterm的小程序。它运行Go服务器,并允许您在浏览器中使用xterm.js与终端进行交互。这个程序很不安全,但是很简单,适合学习。

我复制它是为了让它与最新的xterm.js一起工作,因为它最后一次更新是在6年前。然后,我添加了一些日志语句,打印出每次通过WebSocket发送/接收的字节数。

我们来看看几个不同终端交互过程中的收发情况!

示例:ls

首先,让我们运行ls。以下是我在xterm.js终端上看到的内容:

~:/play$ lsfile~:/play$

下面是发送和接收的内容:(在我的代码中,我记录了客户端每次发送的字节:sent: [bytes],以及它每次从服务器接收的字节: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件事:

回显:客户端发送 l,然后立即收到一个l发送回来。我想这里的意思是,客户端真的很笨 —— 它不知道当我输入l时,我想让l被回显到屏幕上。它必须由服务器进程明确地告诉它来显示它。换行:当我按下回车键时,它发送了一个 \r'(回车)符号,而不是\n'(换行)。转义序列:\x1b是 ASCII 转义字符,所以\x1b[?2004h是告诉终端显示什么或其他东西。我想这是一个颜色序列,但我不确定。我们稍后会详细讨论转义序列。

好,现在让我们做一些稍微复杂一点的事情。

示例:Ctrl+C

接下来,让我们看看当我们用Ctrl+C中断一个进程时会发生什么。下面是我在终端中看到的内容:

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

下面是客户端发送和接收的内容。

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$ "

当我按Ctrl+C时,客户端发送\x03。如果我查ASCII表,\x03是“文本结束”,这似乎是合理的。我认为这真的很酷,因为我一直有点搞不清楚Ctrl+C是如何工作的——知道它只是发送一个\x03字符真好。

我相信当我们按Ctrl+C时,cat被中断的原因是服务器端的Linux内核接收到这个\x03字符,识别出它的意思是“中断”,然后用伪终端向进程组发送一个SIGINT。所以是在内核中处理而不是在用户之间空。

示例:Ctrl+D

让我们尝试完全相同的事情,只是使用Ctrl+D。下面是我在终端上看到的内容:

~:/play$ cat~:/play$

以下是发送和接收的内容:

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很像,只是发送的是\x04而不是\x03。非常好!\x04对应于ASCII“传输结束”。

ctrl+其他字母?

接下来我变得好奇了——如果我发送Ctrl+e,会发送哪些字节?

原来这只是字母表中字母的编号,像这样。

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

另外,Ctrl+Shift+b的作用和Ctrl+b完全一样(写0x2)。

键盘上的其他键呢?这是他们的地图:

Tab-> 0x9(与Ctrl+I相同,因为 I 是第 9 个字母)Escape->\x1bBackspace->\x7fHome->\x1b[HEnd->\x1b[FPrint Screen->\x1b\x5b\x31\x3b\x35\x41Insert->\x1b\x5b\x32\x7eDelete->\x1b\x5b\x33\x7e我的 Meta键完全没有作用

Alt呢?根据我的实验(以及一些搜索),似乎Alt和Escape字面上是一样的,只不过按Alt本身并不会向终端发送任何字符,而按Escape本身会。所以:

alt + d=>\x1bd(其他每个字母都一样)alt + shift + d=>\x1bD(其他每个字母都一样)诸如此类

我们再来看另一个例子!

例如:纳米

以下是我运行文本编辑器nano时发送和接收的内容:

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的东西就是转义序列。来说说转义序列吧!

ANSI转义序列

以上\ x1b[nano发送给客户端的东西称为“转义序列”或“转义码”。这是因为它们都以“转义”字符\x1b开头。他们可以改变光标的位置,使文本加粗或加下划线,改变颜色,等等。维基百科介绍了一些历史,有兴趣可以看看。

我们举个简单的例子:如果你在终端上运行

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

它会打印出“hi there”,其中“hi”为红色,“there”为黑色。在这个页面上有一些关于颜色和格式的转义码的例子。

我认为有几种不同的转义码标准,但我的理解是,人们在Unix上使用的最常见的转义码集来自VT100(博文顶部图片中的老终端),它在过去的40年里没有真正改变过。

转义码是你的终端会乱的原因。如果你把一些二进制数据放到你的屏幕上——通常你会不小心打印出一串随机转义码,这会搞乱你的终端——如果你把足够多的二进制数据放到你的终端上,那里一定有一个0x1b字节。

我可以手动输入转义序列吗?

在前面的小节中,我们讨论了Home键如何映射到\x1b[H]。这3个字节是Escape+[+H(因为Escape是\x1b)。

如果我在xterm.js终端手动输入Escape,那么[,然后H,我会出现在行首,和我按Home一模一样。

我注意到这在我电脑上的Fish shell中不起作用——如果我键入Escape然后回车[,它只是打印出[而不是让我继续对序列进行转义。我问过我的朋友Jesse,他写了一堆Rust终端代码。Jesse告诉我,许多程序都为转义码实现了超时——如果你在某个最小时间内没有按下另一个键,它将决定它实际上不再是转义码。

很明显,这个可以用鱼壳里的fish_escape_delay_ms来配置,所以我运行了set fish_escape_delay_ms 1000,然后就可以手工输入转义码了。工作非常出色!

终端编码有点奇怪。

我想在这里暂停一下。我觉得你按的键映射成字节的方式很奇怪。例如,如果我们今天要从头开始设计键的编码方法,我们可能不会这样设置:

Ctrl + a和Ctrl + Shift + a做的事情完全一样。Alt与Escape是一样的控制序列(如颜色/移动光标)使用与 Escape键相同的字节,因此你需要依靠时间来确定它是一个控制序列还是用户只是想按Escape。

但所有这些都是在70年代或80年代设计的,然后需要永远保持不变,以便向后兼容,所以这就是我们得到的:

更改窗口大小

在终端中,并不是所有你能做的事情都是通过来回发送字节来实现的。例如,当终端被调整大小时,我们必须告诉Linux窗口大小以不同的方式改变了。

下面是goterm中用于执行此操作的Go代码:

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

这是使用ioctl系统调用。我对ioctl的理解是,它是一个系统调用,用来处理一些其他系统调用没有涉及到的随机事情,通常和IO有关,我猜。

系统调用。TIOCSWINSZ是一个整数常量,它告诉ioctl我们希望它在这个例子中做什么(改变终端的窗口大小)。

这也是xterm的工作方式。

在本文中,我们一直在讨论远程终端,即客户端和服务器在不同的计算机上。但事实上,如果使用xterm这样的终端模拟器,所有这些都以完全相同的方式工作,但很难注意到,因为这些字节不是通过网络连接发送的。

这是文章的结尾。

关于终端肯定有很多要了解的(我们可以讨论更多关于颜色,或原始和成熟模式,或Unicode支持,或Linux伪终端接口),但我会在这里停下来,因为现在是晚上10点,这篇文章有点长,我不认为我的大脑可以处理更多关于终端的新信息。

感谢Jesse Luehrs回答了我关于终端的十亿个问题。所有的错误都是我的:

途经:https://jvns.ca/blog/2022/07/20/pseudoterminals/

作者:Julia Evans主题:lujun9972译者:wxy校对:wxy

本文最初由LCTT编辑,Linux中国很荣幸地发布了它。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

作者:美站资讯,如若转载,请注明出处:https://www.meizw.com/n/159948.html

发表回复

登录后才能评论