我可以让ungetc取消阻止阻止fgetc调用吗?

时间:2010-04-12 05:16:08

标签: c stdin blocking

我想在收到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 个答案:

答案 0 :(得分:5)

不是试图通过信号让ungetc()取消阻止阻塞fgetc()调用,也许您可​​以尝试不使用fgetc()阻止开始并等待stdin上的活动{ {1}}。


默认情况下,终端设备的线路规则可能在规范模式下工作。在此模式下,终端驱动程序不会向用户空间显示缓冲区,直到看到换行符(按下 Enter 键)。

要完成您想要的操作,您可以使用select()操作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); } } } ECHO标志会分别在键入字符时禁用字符回显,并直接从输入队列中满足读取请求。在ICANON数组中将VTIMEVMIN的值设置为零会导致读取请求(c_cc)立即返回而不是阻止;有效地轮询stdin。这会导致fgetc()设置为key,因此需要另一种终止循环的方法。通过使用EOF等待stdin上的活动,可以减少对stdin的不必要轮询

执行程序,发送select()信号并输入 t e s t 导致以下输出 1

A
t
e
s
t

1)在Linux上测试

答案 1 :(得分:1)

目标并不完全清楚,但这正是你要找的吗?

#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

答案 2 :(得分:1)

FILE * s不是异步安全的。

您不能在信号处理程序中操作FILE *,而其他人也使用相同的FILE *。你可以在信号处理程序中使用的函数在这里说明:

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

答案 3 :(得分:1)

这与@ 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的{​​{1}}期间收到的处理程序,则还应在退出while循环后检查EOF

另请注意,通常信号处理程序的最佳实践是设置全局变量并从处理程序返回。在你的程序的其他地方,寻找变量并做出适当的反应。