Linux下tcgetattr后调用tcsetattr恢复终端属性失败问题排查
解决tcsetattr恢复终端属性时返回EIO的问题
先修正代码中的明显错误
你提供的代码存在变量名不一致的问题:前面声明的是newtermios,但后续修改属性用的是new_termios,这会导致实际设置的是未修改的原终端属性——虽然这可能不是EIO错误的直接原因,但必须先修正。
EIO错误的可能原因及解决技巧
1. 终端文件描述符失效
操作帧缓冲的过程中,console_fd对应的终端设备可能被切换(比如虚拟控制台切换)、文件描述符被意外关闭,此时对失效的fd调用tcsetattr会返回EIO。
- 解决:
- 恢复属性前,用
fcntl(console_fd, F_GETFD)检查fd是否有效,返回-1则说明fd已失效。 - 失效时,重新打开当前控制终端(
open("/dev/tty", O_RDWR)),用新的fd尝试恢复属性。
- 恢复属性前,用
2. 终端设备状态变更
如果操作帧缓冲期间控制台被切换到其他终端(比如从tty1切到tty2),原console_fd虽然未关闭,但已不再是当前控制终端,调用tcsetattr会失败。
- 解决:
- 恢复前对比当前控制终端与原终端的设备标识:用
fstat分别获取原console_fd和/dev/tty的st_dev和st_ino,不一致则重新打开/dev/tty。
- 恢复前对比当前控制终端与原终端的设备标识:用
3. tcsetattr参数选择问题
TCSANOW参数要求立即设置属性,但如果终端设备状态不稳定(比如输出队列未处理完),可能触发EIO。
- 解决:
- 替换为
TCSAFLUSH(丢弃未处理输入、刷新输出后再设置)或TCSADRAIN(等待输出队列处理完再设置),这两个参数对不稳定的终端兼容性更好。
- 替换为
4. 进程权限变更
操作帧缓冲后,进程可能因降权等原因失去修改终端属性的权限,导致EIO。
- 解决:
- 检查进程有效用户是否属于终端设备的用户组(通常是
tty组),确保有权限修改终端属性。
- 检查进程有效用户是否属于终端设备的用户组(通常是
修正后的示例代码
#include <termios.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include <stdio.h> int main() { int console_fd = open("/dev/tty", O_RDWR); if (console_fd == -1) { perror("open console"); return 1; } struct termios originaltermios; struct termios newtermios; // 保存原终端属性 if (tcgetattr(console_fd, &originaltermios) == -1) { perror("tcgetattr"); close(console_fd); return 1; } // 配置新终端属性(统一变量名) newtermios = originaltermios; newtermios.c_lflag &= ~(ECHO | ISIG | ICANON | IEXTEN); newtermios.c_iflag &= ~(ISTRIP | ICRNL | INLCR | IGNCR | BRKINT | IXON | IXOFF); newtermios.c_cc[VMIN] = 0; newtermios.c_cc[VTIME] = 0; // 应用新属性 tcsdrain(console_fd); if (tcsetattr(console_fd, TCSANOW, &newtermios) == -1) { perror("tcsetattr apply new"); close(console_fd); return 1; } // 操作/dev/fb0的逻辑... sleep(15); // 模拟15秒操作 // 尝试恢复原属性 int ret = tcsetattr(console_fd, TCSAFLUSH, &originaltermios); if (ret == -1 && errno == EIO) { // 原fd失效,重新打开当前控制终端 close(console_fd); console_fd = open("/dev/tty", O_RDWR); if (console_fd == -1) { perror("reopen console"); return 1; } // 再次尝试恢复 ret = tcsetattr(console_fd, TCSAFLUSH, &originaltermios); if (ret == -1) { perror("tcsetattr restore after reopen"); close(console_fd); return 1; } } else if (ret == -1) { perror("tcsetattr restore"); close(console_fd); return 1; } close(console_fd); return 0; }
内容的提问来源于stack exchange,提问作者s_question
相关产品推荐
相关产品推荐

