【问题标题】:Can I make ungetc unblock a blocking fgetc call?我可以让 ungetc 解除阻塞 fgetc 调用吗?
【发布时间】:2010-04-12 05:16:08
【问题描述】:

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

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

无效处理程序(int sig) { ungetc('A',标准输入); } 无效富() { 信号(SIGUSR1,处理程序); 而((键= fgetc(stdin))!= EOF) { ... } }

【问题讨论】:

    标签: c stdin blocking


    【解决方案1】:

    与其尝试让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);
          }
       }
    }
    
    

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

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


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

    一种 吨 e s 吨

    1) 在 Linux 上测试

    【讨论】:

    • 不应该是terminal_settings.c_cc[VMIN] = 1;吗?至少有 1 个字符可用时返回?
    【解决方案2】:

    您的目标并不完全清楚,但这就是您想要的吗?

    #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
    

    【讨论】:

    • 不完全是 - 我正在寻找一种方法让 'A' 领先于 't'。
    【解决方案3】:

    FILE*s 不是异步安全的。

    您不能在信号处理程序中对 FILE* 进行操作,而其他人也使用相同的 FILE*。您可以在信号处理程序中执行的所有功能都在此处说明:

    http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html 。 (有可能 在 Windows 机器上有所不同,但任何 FILE* 在那里也不安全。

    【讨论】:

      【解决方案4】:

      这与@Jamie 的答案基本相同,稍作更改以支持您在t 之前处理A 的愿望,但是在评论框中输入代码太难了,所以我发布了这个答案分开。

      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 的处理程序,您还应该在退出while 循环后检查insert_an_A

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

      【讨论】:

        猜你喜欢
        • 2012-01-20
        • 1970-01-01
        • 2018-06-11
        • 2011-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多