代码之家  ›  专栏  ›  技术社区  ›  Paul Beckingham

我可以让ungetc解除阻止fgetc调用吗?

  •  3
  • Paul Beckingham  · 技术社区  · 14 年前

    我想在收到sigusr1后使用ungetc将“a”字符填充回stdin。想象一下我有一个很好的理由这样做。

    当调用foo()时,在stdin中的阻塞读取不会在收到信号时被ungetc调用中断。虽然我没有预料到这会像现在一样有效,但我想知道是否有一种方法可以实现这一点——有人有什么建议吗?

    void handler (int sig)
    {
      ungetc ('A', stdin);
    }
    
    void foo ()
    {
      signal (SIGUSR1, handler);
    
      while ((key = fgetc (stdin)) != EOF)
      {
        ...
      }
    }
    
    4 回复  |  直到 14 年前
        1
  •  5
  •   jschmier    14 年前

    而不是试图得到 ungetc() 解除阻塞 fgetc() 通过信号呼叫,也许你可以尝试不 fgec()。 块以开始并等待stdin上的活动,使用 select() .


    默认情况下,终端设备的线路规程可以以规范模式工作。在这种模式下,在看到换行符之前,终端驱动程序不会向用户空间提供缓冲区。( 进入 按键)。

    要完成所需的操作,可以使用将终端设置为原始(非规范)模式。 tcsetattr() 操纵 termios 结构。这应该防止阻塞调用 fgec()。 立即返回插入的字符 ungect()号

    
    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 标记分别禁止在键入字符时回显字符,并导致直接从输入队列满足读取请求。设置的值 VTIME VMIN 到零 c_cc 数组导致读取请求( fgec()。 )立即返回而不是阻塞;有效地轮询stdin。这导致了 key 设置为 EOF 因此,另一种终止循环的方法是必要的。通过使用等待stdin上的活动,减少了不必要的stdin轮询 选择() .


    执行程序,发送 SIGUSR1 信号和打字 t e S T 结果如下 :

    A
    t
    e
    s
    t
    

    1)在Linux上测试

        2
  •  1
  •   Jamie    14 年前

    目前还不完全清楚你的目标是什么,但这就是你想要的吗?

    #include <stdio.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int handle = 0;
    
    void handler (int sig) {
      handle = 1;
    }
    
    void foo () {
      int key;
    
      signal (SIGUSR1, handler);
    
      while ((key = fgetc (stdin)) != EOF) {
        printf("%c\n",key);
        if (handle) {
          handle = 0;
          ungetc('A',stdin);
        }
      }
    }
    
    int main(void) {
      printf("PID: %d\n",getpid());
      foo();
    }
    

    它产生这个输出:

    PID: 8902
    test (typed on stdin)
    t
    A
    e
    s
    t
    
        3
  •  1
  •   nos    14 年前

    文件*不是异步安全的。

    当其他人也使用相同的文件*时,不能在信号处理程序中对文件*进行操作。您可以在信号处理程序中使用的所有函数如下所述:

    http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html . (可能是 在Windows计算机上是不同的,但仍然有任何文件*在那里也不安全。

        4
  •  1
  •   Dale Hagglund    14 年前

    这基本上与@jamie的答案相同,稍微改变一下以支持您处理 A t ,但在评论框中键入代码太难了,所以我已经单独发布了这个答案。

    int insert_an_A = 0;
    void handler(int sig) { insert_an_A = 1; }
    
    void process_char(char c) { ... }
    
    int main(int argc, char **argv) {
        int c;
        /* skip signal setup */
        while ((c = fgetc(stdin)) != EOF) {
            if (insert_an_A) {
                process_char('A');
                insert_an_A = 0;
            }
            process_char(c);
        }
    }
    

    如果要处理在调用期间收到的处理程序 fgetc 它会回来的 EOF ,您还应该检查 insert_an_A 退出while循环后。

    注意,一般来说,信号处理程序的最佳实践是设置一个全局变量并从处理程序返回。在程序的其他地方,寻找那个变化的变量并做出适当的反应。