文章目录
1. 前言
The GNU C Library Reference Manual for version 2.35
2. 底层终端接口
本章介绍特定于终端设备的功能。您可以使用这些函数来执行诸如关闭输入回显之类的操作;设置串行线路特性,如线路速度和流量控制;并更改用于文件结尾、命令行编辑、发送信号和类似控制功能的字符。
本章中的大多数函数都对文件描述符进行操作。有关什么是文件描述符以及如何为终端设备打开文件描述符的更多信息,请参阅底层输入/输出。
2.1. 识别终端
Identifying Terminals
本章介绍的功能仅适用于终端设备对应的文件。您可以使用 isatty 函数找出文件描述符是否与终端相关联。
本节中的函数原型在头文件 unistd.h 中声明。
函数:int isatty (int filedes)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
如果filedes 是与打开的终端设备关联的文件描述符,则此函数返回1,否则返回0。
如果文件描述符与终端关联,您可以使用 ttyname 函数获取其关联的文件名。另请参见识别控制终端中描述的 ctermid 函数。
函数:char * ttyname (int filedes)
Preliminary: | MT-Unsafe race:ttyname | AS-Unsafe heap lock | AC-Unsafe lock fd mem | See POSIX Safety Concepts.
如果文件描述符filedes 与终端设备相关联,则ttyname 函数返回一个指向静态分配的、以空结尾的字符串的指针,该字符串包含终端文件的文件名。如果文件描述符未与终端关联,或者无法确定文件名,则该值为空指针。
函数:int ttyname_r (int filedes, char *buf, size_t len)
Preliminary: | MT-Safe | AS-Unsafe heap | AC-Unsafe mem fd | See POSIX Safety Concepts.
ttyname_r 函数类似于 ttyname 函数,不同之处在于它将其结果放入用户指定的缓冲区,该缓冲区从 buf 开始,长度为 len。
ttyname_r 的正常返回值为 0。否则返回错误号以指示错误。为此函数定义了以下 errno 错误条件:
EBADF
filedes 参数不是有效的文件描述符。
ENOTTY
文件不与终端关联。
ERANGE
缓冲区长度 len 太小,无法存储要返回的字符串。
ENODEV
该文件与作为从属伪终端的终端设备相关联,但无法确定与该设备相关联的文件名。这是一个 GNU 扩展。
2.2. I/O 队列
I/O Queues
本节中的许多其余功能都涉及终端设备的输入和输出队列。这些队列在内核中实现了一种缓冲形式,独立于 I/O 流实现的缓冲(请参阅输入/输出流)。
终端输入队列有时也称为其预输入缓冲区。它保存已从终端接收但尚未被任何进程读取的字符。
输入队列的大小由 MAX_INPUT 和 _POSIX_MAX_INPUT 参数描述;请参阅文件系统容量限制。保证队列大小至少为 MAX_INPUT,但队列可能更大,甚至可能动态更改大小。如果通过设置 IXOFF 输入模式位(请参阅输入模式)启用输入流控制,则终端驱动程序会在必要时将 STOP 和 START 字符传输到终端以防止队列溢出。否则,如果从终端输入太快,输入可能会丢失。在规范模式下,所有输入都保留在队列中,直到收到换行符为止,因此当您键入很长的行时,终端输入队列可能会填满。请参阅两种输入方式:规范或非规范。
终端输出队列和输入队列一样,只是用于输出;它包含已由进程写入但尚未传输到终端的字符。如果通过设置 IXON 输入模式位(请参阅输入模式)启用输出流控制,则终端驱动程序遵循终端发送的 START 和 STOP 字符来停止和重新启动输出传输。
清除终端输入队列意味着丢弃任何已接收但尚未读取的字符。同样,清除终端输出队列意味着丢弃任何已写入但尚未传输的字符。
2.3. 两种输入方式:规范或非规范
Two Styles of Input: Canonical or Not
POSIX 系统支持两种基本的输入模式:规范和非规范。
在规范输入处理模式中,终端输入在以换行符 (‘\n’)、EOF 或 EOL 字符终止的行中处理。在用户键入整行之前,无法读取输入,并且无论请求多少字节,读取函数(请参阅输入和输出原语)最多返回单行输入。
在规范输入模式下,操作系统提供输入编辑函数:一些字符被专门解释以在当前文本行内执行编辑操作,例如 ERASE 和 KILL。请参阅用于输入编辑的字符。
常量 _POSIX_MAX_CANON 和 MAX_CANON 参数化可能出现在单行规范输入中的最大字节数。请参阅文件系统容量限制。保证最大行长度至少为 MAX_CANON 字节,但最大值可能更大,甚至可能动态更改大小。
在非规范输入处理模式下,字符不分组为行,不执行 ERASE 和 KILL 处理。在非规范输入模式下读取字节的粒度由 MIN 和 TIME 设置控制。请参阅非规范输入。
大多数程序使用规范输入模式,因为这为用户提供了一种逐行编辑输入的方法。使用非规范模式的通常原因是程序接受单字符命令或提供自己的编辑工具。
规范或非规范输入的选择由 struct termios 的 c_lflag 成员中的 ICANON 标志控制。请参阅本地模式。
2.4. 终端模式
Terminal Modes
本节介绍控制输入和输出完成方式的各种终端属性。函数、数据结构和符号常量都在头文件 termios.h 中声明。
不要将终端属性与文件属性混淆。与终端关联的设备专用文件具有文件属性中所述的文件属性。这些与本节讨论的终端设备本身的属性无关。
2.4.1. 终端模式数据类型
Terminal Mode Data Types
终端的整个属性集合存储在 struct termios 类型的结构中。此结构与函数 tcgetattr 和 tcsetattr 一起使用以读取和设置属性。
数据类型:struct termios
struct termios 记录了终端的所有 I/O 属性。该结构至少包括以下成员:
tcflag_t c_iflag
指定输入模式标志的位掩码;请参阅输入模式。
tcflag_t c_oflag
指定输出模式标志的位掩码;请参阅输出模式。
tcflag_t c_cflag
指定控制模式标志的位掩码;请参阅控制模式。
tcflag_t c_lflag
指定本地模式标志的位掩码;请参阅本地模式。
cc_t c_cc[NCCS]
一个数组,指定哪些字符与各种控制功能相关联;请参阅特殊字符。
struct termios 结构还包含对输入和输出传输速度进行编码的成员,但未指定表示形式。有关如何检查和存储速度值的信息,请参见行速度。
以下部分描述了 struct termios 结构成员的详细信息。
数据类型:tcflag_t
这是一个无符号整数类型,用于表示终端标志的各种位掩码。
数据类型:cc_t
这是一个无符号整数类型,用于表示与各种终端控制功能相关的字符。
宏:int NCCS
该宏的值是 c_cc 数组中的元素数。
2.4.2. 终端模式功能
Terminal Mode Functions
函数:int tcgetattr (int filedes, struct termios *termios-p)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
该函数用于检查具有文件描述符文件的终端设备的属性。属性以 termios-p 指向的结构返回。
如果成功,则 tcgetattr 返回 0。返回值 -1 表示错误。为此函数定义了以下 errno 错误条件:
EBADF
filedes 参数不是有效的文件描述符。
ENOTTY
文件不与终端关联。
函数:int tcsetattr (int filedes, int when, const struct termios *termios-p)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
该函数使用文件描述符文件设置终端设备的属性。新属性取自 termios-p 指向的结构。
when 参数指定如何处理已经排队的输入和输出。它可以是以下值之一:
TCSANOW
立即进行更改。
TCSADRAIN
等到所有排队的输出都被写入后进行更改。在更改影响输出的参数时,您通常应该使用此选项。
TCSAFLUSH
这类似于 TCSADRAIN,但也会丢弃任何排队的输入。
TCSASOFT
这是一个标志位,您可以将其添加到上述任何替代方案中。其含义是禁止改变终端硬件的状态。它是一个 BSD 扩展;它仅在 BSD 系统和 GNU/Hurd 系统上受支持。
使用 TCASSOFT 与在 termios-p 指向的结构的 c_cflag 成员中设置 CIGNORE 位完全相同。有关 CIGNORE 的说明,请参阅控制模式。
如果从其控制终端上的后台进程调用此函数,通常会向进程组中的所有进程发送一个 SIGTTOU 信号,就像进程试图写入终端一样。例外情况是调用进程本身忽略或阻塞 SIGTTOU 信号,在这种情况下执行操作并且不发送信号。请参阅作业控制。
如果成功,则 tcsetattr 返回 0。返回值 -1 表示错误。为此函数定义了以下 errno 错误条件:
EBADF
filedes 参数不是有效的文件描述符。
ENOTTY
文件不与终端关联。
EINVAL
when 参数的值无效,或者 termios-p 参数中的数据有问题。
尽管 tcgetattr 和 tcsetattr 使用文件描述符指定终端设备,但属性是终端设备本身的属性,而不是文件描述符的属性。这意味着改变终端属性的效果是持久的;如果稍后另一个进程打开终端文件,它将看到更改的属性,即使它与您最初在更改属性时指定的打开文件描述符没有任何关系。
同样,如果单个进程对同一终端设备具有多个或重复的文件描述符,则更改终端属性会影响所有这些文件描述符的输入和输出。这意味着,例如,您无法在正常的行缓冲、回显模式下打开一个文件描述符或流以从终端读取;并同时为您用来以单字符、非回显模式读取的同一终端拥有另一个文件描述符。相反,您必须明确地在两种模式之间来回切换终端。
2.4.3. 正确设置终端模式
Setting Terminal Modes Properly
当你设置终端模式时,你应该首先调用 tcgetattr 来获取特定终端设备的当前模式,只修改你真正感兴趣的那些模式,然后用 tcsetattr 存储结果。
简单地将 struct termios 结构初始化为一组选定的属性并将其直接传递给 tcsetattr 是一个坏主意。您的程序可能会在数年后在支持本手册中未记录的成员的系统上运行。避免将这些成员设置为不合理值的方法是避免更改它们。
此外,不同的终端设备可能需要不同的模式设置才能正常运行。所以你应该避免盲目地将属性从一个终端设备复制到另一个终端设备。
当一个成员包含一组独立标志时,就像 c_iflag、c_oflag 和 c_cflag 成员所做的那样,即使设置整个成员也是一个坏主意,因为特定的操作系统有自己的标志。相反,您应该从成员的当前值开始,只更改值在您的程序中重要的标志,而保持任何其他标志不变。
这是一个示例,说明如何在 struct termios 结构中设置一个标志 (ISTRIP),同时正确保留结构中的所有其他数据:
int
set_istrip (int desc, int value)
{
struct termios settings;
int result;
result = tcgetattr (desc, &settings);
if (result < 0)
{
perror ("error in tcgetattr");
return 0;
}
settings.c_iflag &= ~ISTRIP;
if (value)
settings.c_iflag |= ISTRIP;
result = tcsetattr (desc, TCSANOW, &settings);
if (result < 0)
{
perror ("error in tcsetattr");
return 0;
}
return 1;
}
2.4.4. 输入模式
Input Modes
本节描述控制输入处理的相当低级方面的终端属性标志:奇偶校验错误、中断信号、流控制以及 RET 和 LFD 字符的处理。
所有这些标志都是 struct termios 结构的 c_iflag 成员中的位。该成员是一个整数,您可以使用运算符 &、| 更改标志和^。不要试图为 c_iflag 指定整个值——相反,只更改特定的标志,其余的保持不变(请参阅

本文深入探讨了终端接口,包括规范和非规范输入模式、流控制、特殊字符如擦除和杀死字符,以及如何设置和管理终端属性。还介绍了BSD终端模式、行控制函数以及伪终端的使用,为理解和控制终端设备提供了全面的指南。
最低0.47元/天 解锁文章
949

被折叠的 条评论
为什么被折叠?



