与其尝试让ungetc() 通过信号解除阻塞fgetc() 调用,也许您可以尝试不使用fgetc() 阻塞并使用select() 等待标准输入上的活动。
默认情况下,终端设备的线路规则可以在规范模式下工作。在这种模式下,终端驱动程序不会将缓冲区呈现给用户空间,直到看到换行符(按下 Enter 键)。
要完成您想要的,您可以通过使用@987654321@ 操作termios 结构将终端设置为原始(非规范)模式。这应该阻止对fgetc() 的调用,以立即返回使用ungetc() 插入的字符。
void handler(int sig) {
/* I know I shouldn't do this in a signal handler,
* but this is modeled after the OP's code.
*/
ungetc('A', stdin);
}
void wait_for_stdin() {
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fileno(stdin),&fdset);
select(1, &fdset, NULL, NULL, NULL);
}
void foo () {
int key;
struct termios terminal_settings;
signal(SIGUSR1, handler);
/* set the terminal to raw mode */
tcgetattr(fileno(stdin), &terminal_settings);
terminal_settings.c_lflag &= ~(ECHO|ICANON);
terminal_settings.c_cc[VTIME] = 0;
terminal_settings.c_cc[VMIN] = 0;
tcsetattr(fileno(stdin), TCSANOW, &terminal_settings);
for (;;) {
wait_for_stdin();
key = fgetc(stdin);
/* terminate loop on Ctrl-D */
if (key == 0x04) {
break;
}
if (key != EOF) {
printf("%c\n", key);
}
}
}
注意:为简单起见,此代码省略了错误检查。
分别清除ECHO 和ICANON 标志会在键入字符时禁用回显字符,并导致直接从输入队列中满足读取请求。在c_cc 数组中将VTIME 和VMIN 的值设置为零会导致读取请求(fgetc())立即返回而不是阻塞;有效地轮询标准输入。这会导致key 设置为EOF,因此需要另一种终止循环的方法。通过使用 select() 等待标准输入上的活动来减少不必要的标准输入轮询。
执行程序,发送SIGUSR1 信号,然后输入
t e s t 导致以下输出1:
一种
吨
e
s
吨
1) 在 Linux 上测试