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

发布于 2024-08-28 16:21:28 字数 324 浏览 9 评论 0原文

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

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

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

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

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

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.

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)
  {
    ...
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

手长情犹 2024-09-04 16:21:28

与其尝试通过信号让 ungetc() 解锁阻塞的 fgetc() 调用,也许您可​​以尝试不使用 fgetc()使用 select() 阻止开始并等待标准输入上的活动。


默认情况下,终端设备的线路规则可以工作在规范模式下。在此模式下,终端驱动程序不会将缓冲区呈现给用户空间,直到看到换行符(按下 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 标志会禁用键入字符时的回显,并导致直接从输入队列满足读取请求。将 c_cc 数组中的 VTIMEVMIN 值设置为零会导致读取请求 (fgetc())立即返回而不是阻塞;有效地轮询标准输入。这会导致 key 设置为 EOF,因此需要另一种终止循环的方法。通过使用 select() 等待标准输入上的活动,可以减少不必要的标准输入轮询。

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

A
t
e
s
t

1 )在 Linux 上测试

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().


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).

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);
      }
   }
}

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) tested on Linux

秋风の叶未落 2024-09-04 16:21:28

目前尚不完全清楚您的目标是什么,但这就是您正在寻找的吗?

#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

It is not entirely clear what your goal is, but is this what you are looking for?

#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();
}

It produces this output:

PID: 8902
test (typed on stdin)
t
A
e
s
t
贪了杯 2024-09-04 16:21:28

FILE* 不是异步安全的。

当其他人也使用同一个 FILE* 时,您不能在信号处理程序中操作 FILE*。您可以在信号处理程序中使用的所有功能如下:

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

FILE*s are not async safe.

You cannot operate on a FILE* in a signal handler while someone else also uses that same FILE*. functions you can all in a signal handler is stated here:

http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html . (It might be
different on a windows machine, but still any FILE* are not safe there either.

楠木可依 2024-09-04 16:21:28

这本质上与 @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

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

This is essentially the same as @Jamie's answer, slightly changed to support your desire to process the A before the t, but it's too hard to type code into a comment box, so I've posted this answer separately.

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);
    }
}

If you want to process an handler received during the call to fgetc that returns EOF, you should also check insert_an_A after exiting the while loop.

Note also that in general the best practice for signal handlers is to set a global variable and return from the handler. Elsewhere in your program, look for that variable changing and react appropriately.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文