有些语言具有宽字符。宽字符可以有多个字节。当您在控制台或 X 中键入宽字符时,您实际上正在发送几个字节。单字节字符本身是原子的,从某种意义上说,它要么传输,要么不传输,要么接收,要么不接收。但对于宽字符来说,情况并非如此。例如,仅传送 3 字节字符的第一个字节将生成垃圾。底层系统如何保证应用程序始终自动接收宽字符?一个好的答案应该解释当用户分别在控制台、X 和 ssh 中键入宽字符时依次发生的情况。开始故事:当用户输入宽字符时,会生成一个中断......
底层系统如何保证应用程序始终自动接收宽字符?
由于堆栈中有多个层,因此这个问题中的术语可能会让一些人感到困惑。我真正的意思是:想想你正在编写一个带有编辑框的 GUI 应用程序。当您键入宽字符时,它要么完整显示,要么不显示,从不部分显示。因此底层系统包括应用程序下面的所有内容(在本例中为应用程序框架、GUI 库等)。
答案1
他们不是。
(文本)应用程序在标准输入上接收字节流。该应用程序可以随意阅读任意数量的内容,而且确实如此调用read
可以自由返回少于请求的字节数。可以随意解释该字节流,请求读取更多字节,或将所有内容视为不透明的二进制数据。如果它想要将某些字节解释为一个不同的离散块的一部分,它能够这样做,并且它能够继续查找,直到它决定它拥有该块。没有什么能保证它立刻得到所有除非它是一个字节。
这是简短的答案,如果您愿意,您现在可以停止阅读:问题的前提是错误的,直到应用程序能够做任何它想做的事情来获得它选择的结果。
下面我们将继续讨论事情可能(或可能不会)发生的一些方式,以及从某人的角度来看它们何时可能是“原子的”,也可能不是“原子的”。
对于这个答案的其余部分,我们将忽略网络和套接字流量。我们还将对“角色”采用一个相当模糊的定义,因为这似乎是起点,并且可能会在我们进行过程中对其进行完善。我们将从最终应用程序的角度考虑问题。最后,我们将简要介绍一下从键盘硬件开始的(主要是偏离主题的)路径。时间还是太长了。
应用程序想要获取输入字符
该申请要求read
从其输入中获取一些字节。然后,它还可以自由地以任何它喜欢的方式解释它收到的字节,或者让一个库为它这样做。在需要文本的情况下,它将通过某些方式解释这些字节字符编码,它决定了字节序列的含义。希望它与数据源或终端就所使用的编码达成一致(但这不是必须的!)。对于任何编码,应用程序决定将事物解释为:
如果该编码是 ASCII,那么一切都很好,因为无论如何一切都是单字节的。
如果该编码是 UCS-2,对于 16 位
wchar_t
,它最好知道它已经读取了多少字节,以便它可以在需要时请求另一个字节(因为read
可能随时返回奇数个字节,给你一半一个代码单元)。如果该编码是UTF-9,它可能需要对自己进行一些修改才能取出代码单元,并且在现代系统上几乎不可能以原子方式交付它。
如果该编码是 KEIS,那么您期望的级别的原子传递甚至是不可能的,因为序列的解释取决于流中较早的有状态模式转换。应用程序需要记住它已经看到的内容才能知道这段输入的含义。
如果该编码是UTF-8如今,应用程序极有可能可以使用任何单个字节进行同步,因为它是一种自同步编码:对于单字节代码单元,高位为 0;对于多字节序列的任何部分,高位为 1。 2、3 或 4 字节代码单元的第一个字节分别为 110、1110 或 11110,后续字节从 10 开始。读取一个字节会告诉您还需要多少个字节,或者您处于已经在某事的中间了。
如有需要,可申请那么可能希望做一个第二
read
对于以下字节。它需要记住已经读取的部分元素,并在最后将两部分组合在一起。它甚至可能需要进行第三次或第四次读取。然后,它可以根据需要离散地处理它们,也许会为代码点发出单个 32 位值。将其抽象为函数或库以供重用可能是明智的。
还有其他编码可具有所有这些属性,甚至更多。其中一些不被 POSIX 允许作为系统编码,但可能在应用程序之间达成一致。如果您现在在类 Unix 系统上使用多字节系统编码,则很可能是 UTF-8。
但角色到底是什么?
我的多字节字符“é”可能是 UTF-8 的两个字节 (C3 A9) 或三个字节 (65 CC 81),因为它可能是单个代码点 (U+00E9 LATIN SMALL LETTER E with ACUTE) 或两个 ( U+0065 拉丁文小写字母 E + U+0301 组合锐音)。 ”