我想在收到 SIGUSR1 后使用 ungetc 将“A”字符填回标准输入。想象一下,我有充分的理由这样做。

调用 foo() 时,stdin 中的阻塞读取不会在收到信号时被 ungetc 调用中断。虽然我没想到这会按原样工作,但我想知道是否有办法实现这一目标 - 有没有人有建议?

无效处理程序(int sig)
{
ungetc ('A', stdin);
}

空富()
{
信号(SIGUSR1,处理程序);

while ((key = fgetc (stdin)) != EOF)
{
...
}
}

最佳答案

与其尝试让 ungetc() 通过信号解除阻塞的 fgetc() 调用,也许您可​​以尝试不让 fgetc() 块开始并使用 select() 等待 stdin 上的事件。

默认情况下,终端设备的线路规则可以在规范模式下工作。在这种模式下,终端驱动程序不会将缓冲区呈现给用户空间,直到看到换行符(按下 Enter 键)。

要完成您想要的操作,您可以通过使用 tcsetattr() 操作 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);
      }
   }
}


注意:为简单起见,此代码省略了错误检查。

分别清除 ECHOICANON 标志会禁用字符在键入时的回显,并导致直接从输入队列中满足读取请求。在 VTIME 数组中将 VMINc_cc 的值设置为零会导致读取请求( fgetc() )立即返回而不是阻塞;有效地轮询标准输入。这会导致 key 设置为 EOF,因此需要另一种终止循环的方法。通过使用 select() 等待 stdin 上的事件减少了对 stdin 的不必要轮询。

执行程序,发送 SIGUSR1 信号,然后输入
t e s t 产生以下输出1:

一种

电子



1)在Linux上测试

关于我可以让 ungetc 解除阻塞的 fgetc 调用吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2620010/

10-11 21:27