我想在收到 SIGUSR1 后使用 ungetc 将“A”字符填回标准输入。想象一下,我有充分的理由这样做。
调用 foo() 时,stdin 中的阻塞读取不会在收到信号时被 ungetc 调用中断。虽然我没想到这会按原样工作,但我想知道是否有办法实现这一目标 - 有没有人有建议?
无效处理程序(int sig)
{
ungetc ('A', stdin);
}
空富()
{
信号(SIGUSR1,处理程序);
while ((key = fgetc (stdin)) != EOF)
{
...
}
}
最佳答案
与其尝试让 ungetc()
通过信号解除阻塞的 fgetc()
调用,也许您可以尝试不让 fgetc()
块开始并使用 select()
等待 stdin 上的事件。
默认情况下,终端设备的线路规则可以在规范模式下工作。在这种模式下,终端驱动程序不会将缓冲区呈现给用户空间,直到看到换行符(按下 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);
}
}
}
注意:为简单起见,此代码省略了错误检查。
分别清除
ECHO
和 ICANON
标志会禁用字符在键入时的回显,并导致直接从输入队列中满足读取请求。在 VTIME
数组中将 VMIN
和 c_cc
的值设置为零会导致读取请求( fgetc()
)立即返回而不是阻塞;有效地轮询标准输入。这会导致 key
设置为 EOF
,因此需要另一种终止循环的方法。通过使用 select()
等待 stdin 上的事件减少了对 stdin 的不必要轮询。执行程序,发送
SIGUSR1
信号,然后输入t e s t 产生以下输出1:
一种
吨
电子
秒
吨
1)在Linux上测试
关于我可以让 ungetc 解除阻塞的 fgetc 调用吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2620010/