我可以让ungetc取消阻止fgetc调用吗? [英] Can I make ungetc unblock a blocking fgetc call?

查看:76
本文介绍了我可以让ungetc取消阻止fgetc调用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在收到SIGUSR1时使用ungetc将'A'字符填充回stdin中.想象一下,我有这样做的充分理由.

I would like to stuff an 'A' character back into stdin using ungetc on receipt of SIGUSR1. Imagine that I have a good reason for doing this.

当调用foo()时,接收到信号后ungetc调用不会中断stdin中读取的阻塞.虽然我没想到它会如此工作,但我想知道是否有办法实现这一目标-有人有建议吗?

When calling foo(), the blocking read in stdin is not interrupted by the ungetc call on receipt of the signal. While I didn't expect this to work as is, I wonder if there is a way to achieve this - does anyone have suggestions?


void handler (int sig)
{
  ungetc ('A', stdin);
}

void foo ()
{
  signal (SIGUSR1, handler);

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

推荐答案

与其尝试通过信号使ungetc()取消阻塞fgetc()调用,不如尝试不让fgetc()块开始并使用select()等待在stdin上的活动.

Rather than try to get ungetc() to unblock a blocking fgetc() call via a signal, perhaps you could try not having fgetc() block to begin with and wait for activity on stdin using select().

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

By default, the line discipline for a terminal device may work in canonical mode. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enter key is pressed).

要完成所需的操作,可以通过使用tcsetattr()操纵termios结构将终端设置为原始(非规范)模式.这样可以防止对fgetc()的阻塞调用立即返回用ungetc()插入的字符.

To accomplish what you want, you can set the terminal into raw (non-canonical) mode by using tcsetattr() to manipulate the termios structure. This should case the blocking call to fgetc() to immediately return the character inserted with 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标志会禁用键入字符时的回显,并导致直接从输入队列中满足读取请求.将c_cc数组中的VTIMEVMIN的值设置为零会导致读取请求(fgetc())立即返回而不是阻塞;有效地轮询标准输入.这导致key设置为EOF,因此需要另一种终止循环的方法.通过使用select()等待stdin上的活动,可以减少对stdin的不必要轮询.

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

NOTE: This code omits error checking for simplicity.

Clearing the ECHO and ICANON flags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIME and VMIN to zero in the c_cc array causes the read request (fgetc()) to return immediately rather than block; effectively polling stdin. This causes key to get set to EOF so another method for terminating the loop is necessary. Unnecessary polling of stdin is reduced by waiting for activity on stdin using select().


Executing the program, sending a SIGUSR1 signal, and typing t e s t results in the following output1:


A
t
e
s
t

1)在Linux上进行了测试

这篇关于我可以让ungetc取消阻止fgetc调用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆