Linux---终端 I/O 控制接口开发<termios.h>终端输入输出系统
<termios.h>
是 POSIX 标准 中定义的终端 I/O 控制接口,主要用于 Unix/Linux 系统下配置和控制终端设备(TTY)的行为。它是对传统 Unix termio
接口的改进版本,在 BSD 和 System V 系统中都有支持,并被纳入 POSIX.1 标准。
在现代 Linux 系统中,<termios.h>
是控制终端属性的核心头文件,常用于:
- 串口通信参数配置(波特率、数据位、校验位等)
- 终端输入模式切换(规范模式 / 非规范模式 / raw 模式)
- 关闭或开启输入回显(Echo)
- 自定义特殊控制字符(如 Ctrl+C、Ctrl+Z)
- 实时输入处理(游戏、实时监控、快捷键响应)
可以说,只要在 Linux/Unix 下精确控制终端或串口,<termios.h>
就是绕不开的工具。
2. 核心数据结构:struct termios
<termios.h>
的所有配置都围绕 struct termios
结构体展开。它保存了终端的全部属性:
struct termios {tcflag_t c_iflag; // 输入模式标志 (input flags)tcflag_t c_oflag; // 输出模式标志 (output flags)tcflag_t c_cflag; // 控制模式标志 (control flags)tcflag_t c_lflag; // 本地模式标志 (local flags)cc_t c_cc[NCCS]; // 控制字符数组 (control characters)speed_t c_ispeed; // 输入波特率 (POSIX.1 不强制要求)speed_t c_ospeed; // 输出波特率
};
2.1 c_iflag
(输入模式标志)
控制终端接收数据时的处理方式,常见标志:
标志 | 作用 |
---|---|
IGNBRK | 忽略输入中的中断条件(break condition) |
BRKINT | 当收到 break 时,产生 SIGINT 信号 |
ICRNL | 将输入中的 CR(回车)转换为 NL(换行) |
IXON | 启用输出软件流控制(XON/XOFF 协议) |
IXOFF | 启用输入软件流控制 |
INPCK | 启用奇偶校验检测 |
ISTRIP | 将 8 位字符的最高位清除(变为 7 位) |
2.2 c_oflag
(输出模式标志)
控制终端发送数据时的处理方式,常见标志:
标志 | 作用 |
---|---|
OPOST | 启用输出处理(如 NL→CR+NL 转换) |
ONLCR | 将 NL 转换为 CR+NL |
OCRNL | 将 CR 转换为 NL |
ONOCR | 在第 0 列不输出 CR |
ONLRET | NL 执行 CR 功能 |
2.3 c_cflag
(控制模式标志)
控制终端硬件特性,尤其是串口参数:
标志 | 作用 |
---|---|
CLOCAL | 忽略调制解调器控制线路(本地连接) |
CREAD | 启用接收数据 |
CSIZE | 数据位掩码(需配合 CS5/CS6/CS7/CS8 使用) |
CS8 | 8 位数据位 |
PARENB | 启用奇偶校验位 |
PARODD | 使用奇校验(默认偶校验) |
CSTOPB | 2 个停止位(默认 1 个) |
HUPCL | 关闭设备时挂断(hang up)调制解调器 |
2.4 c_lflag
(本地模式标志)
控制终端本地行为(不涉及硬件线路):
标志 | 作用 |
---|---|
ICANON | 启用规范模式(行缓冲) |
ECHO | 启用输入字符回显 |
ECHOE | 当输入 ERASE 字符时,删除前一个字符并刷新显示 |
ECHOK | 当输入 KILL 字符时,清空当前行并刷新显示 |
ISIG | 启用信号生成(如 Ctrl+C 产生 SIGINT) |
IEXTEN | 启用扩展功能(如特殊函数键) |
2.5 c_cc
(控制字符数组)
定义特殊控制字符,通过下标访问,常见下标:
下标 | 含义 | 默认值 |
---|---|---|
VINTR | 中断字符 | Ctrl+C |
VQUIT | 退出字符 | Ctrl+\ |
VERASE | 删除字符 | Backspace |
VKILL | 行删除字符 | Ctrl+U |
VEOF | 文件结束字符 | Ctrl+D |
VMIN | 非规范模式下最小读取字符数 | 1 |
VTIME | 非规范模式下超时时间(0.1 秒为单位) | 0 |
3. 主要 API 函数
3.1 获取和设置终端属性
#include <termios.h>
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
fd
:终端设备文件描述符(通常是STDIN_FILENO
表示标准输入)optional_actions
:生效时机TCSANOW
:立即生效TCSADRAIN
:等待输出完成后生效TCSAFLUSH
:等待输出完成,并丢弃未读输入后生效
3.2 波特率设置
speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
常见 speed
值:B9600
, B19200
, B38400
, B115200
等。
3.3 模式切换函数
void cfmakeraw(struct termios *termios_p);
将终端设置为 raw 模式(非规范、无回显、无信号处理)。
3.4 队列刷新
int tcflush(int fd, int queue_selector);
TCIFLUSH
:清空输入队列TCOFLUSH
:清空输出队列TCIOFLUSH
:清空输入和输出队列
3.5 发送 Break 信号
int tcsendbreak(int fd, int duration);
- 持续发送 0 值比特流(break 信号)
4. 终端工作模式详解
4.1 规范模式(Canonical Mode)
- 输入以 行 为单位缓冲,用户按 Enter 后才交给程序
- 支持行编辑(删除、退格、整行删除)
- 可以通过特殊字符产生信号(Ctrl+C 中断)
- 默认模式
4.2 非规范模式(Non-canonical Mode)
- 输入不经过行缓冲,直接发送给程序
- 常用于实时响应场景(游戏、串口调试)
- 由
VMIN
和VTIME
控制读取行为:VMIN = 0, VTIME = 0
:非阻塞读取,有数据就读,无数据就返回 0VMIN > 0, VTIME = 0
:阻塞直到读取到VMIN
个字符VMIN = 0, VTIME > 0
:等待数据,超时返回 0VMIN > 0, VTIME > 0
:读取到第一个字符后开始计时,超时前读到VMIN
个字符则返回,否则返回已读字符数
4.3 Raw 模式
- 非规范模式的一种极端形式
- 关闭所有输入输出处理(不转换 CR/NL,不回显,不产生信号)
- 常用于需要完全控制输入输出的场景
5. 代码示例
5.1 将终端设为非规范模式(无回显)
#include <termios.h>
#include <unistd.h>
#include <iostream>int main() {struct termios oldt, newt;tcgetattr(STDIN_FILENO, &oldt); // 保存旧设置newt = oldt;// 禁用规范模式和回显newt.c_lflag &= ~(ICANON | ECHO);// 非规范模式读取参数newt.c_cc[VMIN] = 1; // 至少读取 1 个字符newt.c_cc[VTIME] = 0; // 不超时tcsetattr(STDIN_FILENO, TCSANOW, &newt);std::cout << "请输入字符(按 q 退出):" << std::endl;char c;while (true) {read(STDIN_FILENO, &c, 1);if (c == 'q') break;std::cout << "你输入了: " << c << std::endl;}tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // 恢复原设置return 0;
}
5.2 串口通信配置示例
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>int main() {int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);if (fd < 0) {perror("无法打开串口");return 1;}struct termios tio;memset(&tio, 0, sizeof(tio));// 配置控制模式tio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;// 无奇偶校验tio.c_cflag &= ~PARENB;// 1 个停止位tio.c_cflag &= ~CSTOPB;// 禁用输入处理tio.c_iflag = 0;// 禁用输出处理tio.c_oflag = 0;// 禁用本地模式(非规范、无回显、无信号)tio.c_lflag = 0;// 非规范模式读取参数tio.c_cc[VMIN] = 1;tio.c_cc[VTIME] = 0;// 应用设置tcflush(fd, TCIFLUSH);tcsetattr(fd, TCSANOW, &tio);// 发送数据const char* msg = "Hello Serial\n";write(fd, msg, strlen(msg));// 读取数据char buf[256];ssize_t n = read(fd, buf, sizeof(buf));if (n > 0) {buf[n] = '\0';printf("收到: %s", buf);}close(fd);return 0;
}
6. 高级主题
6.1 信号与终端
在规范模式下,特殊字符(如 Ctrl+C)会产生信号。如果想捕获这些信号,需要:
- 保留
ISIG
标志 - 使用
signal()
或sigaction()
设置信号处理函数
6.2 伪终端(Pseudoterminal)
<termios.h>
不仅用于物理终端,还可用于伪终端(PTY),常用于:
- 终端模拟器
- 远程登录服务(SSH)
- 容器的 TTY 分配
6.3 多平台兼容性
<termios.h>
主要在 Linux/Unix 系统可用- 在 Windows 下需要使用 WinAPI(如
SetConsoleMode
) - 跨平台项目可使用 ncurses 库或自定义封装层
总结与注意事项
<termios.h>
提供了对终端设备的底层控制能力,涵盖:
- 终端模式切换(规范 / 非规范 / raw)
- 输入输出数据处理配置
- 串口通信参数设置
- 控制字符自定义
- 信号与中断处理
使用时的注意事项:
- 保存并恢复原设置:程序退出前必须恢复终端原属性,否则可能影响用户后续操作
- 位操作修改标志:使用
|=
添加标志,&= ~
清除标志 - 选择正确的生效时机:根据需求选择
TCSANOW
、TCSADRAIN
或TCSAFLUSH
- 错误处理:所有
<termios.h>
函数都有返回值,应检查错误