c程序中的非规范终端模式缓冲区stdout
Non canonical terminal mode buffer stdout in c program
我正在做一个学校项目(构建一个非常基础的 shell)。
我们的想法是能够像 bash 那样进行行版。为此,我将终端模式更改为非规范模式并停止回显。
我编写了一个非常简单的代码来暴露我的问题(请注意,我会检查函数 returns 等等......我只是为了这个 post 让它尽可能短)
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int ret;
char buff;
char *term_name;
char *termcap;
struct termios termios_new;
struct termios termios_backup;
/*
** Init termcap library
*/
term_name = getenv("TERM");
tgetent(NULL, term_name);
/*
** Get the terminal mode to non canonical and shut down echo
*/
bzero(&termios_new, sizeof(struct termios));
tcgetattr(STDIN_FILENO, &termios_backup);
termios_new = termios_backup;
termios_new.c_lflag &= ~(ICANON);
termios_new.c_lflag &= ~(ECHO);
termios_new.c_cc[VMIN] = 1;
termios_new.c_cc[VTIME] = 0;
/*
** Set the change
*/
tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_new);
/*
** Get the termcap for clearing screen on the particular terminal
*/
termcap = tgetstr("cl", NULL);
/*
** Loop read to get user entries and clear screen for 'c', output char for 'b', break for 'q'
*/
while((ret = read(STDIN_FILENO, &buff, 1)) > 0)
{
if (buff == 'c')
tputs(termcap, 1, putchar);
else if (buff == 'b')
putchar(buff);
else if (buff == 'q')
break ;
buff = 0;
}
/*
** Put back the terminal mode as found before
*/
tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_backup);
return (0);
}
所以基本上它是一个读取循环以捕获用户条目。它清除 'c' 的屏幕,'b' 的输出字符,中断并恢复 'q'.
的原始终端模式
问题是:
每当我键入任何内容时,它似乎都被缓冲了,因为在我用 'q' 打破循环之前什么都没有发生。
此时,输出显示在屏幕上,如果我键入 5 次 b,我将得到 5 个 b,如果我键入 'c',屏幕将被清除。
但是,只有在输入 'q' 之后。
无论是否恢复原始终端模式,行为都是相同的。 (return
之前的最后一行)
我怀疑的是:
在使代码非常短并检查所有 returns 之后,我倾向于认为只能是我更改终端模式的方式有问题?
我尝试为 tcsetattr
函数使用标志 TCSAFLUSH
和 TCSADRAIN
,结果相同。
谢谢! :)
✅ 已解决:
好吧,对于遇到这种情况的任何人来说,这很有趣,因为它让我学到了很多东西(好吧,这是一个学校项目,所以...)。
在 tputs
参数中使用 putchar
是问题所在,因为 putchar 是缓冲的,就像 printf 一样。
我最终尝试了两件事,让我意识到:fflush()
在 tputs
函数调用起作用之后,显然问题与缓冲区相关。
然后尝试输出到 STDERR_FILENO,它也有点解决了,事实上 stderr 是唯一的 _IONBF
,所以没有缓冲。
所以我最终创建了一个只有 write
的 putchar 函数,仅此而已。
以下是有关三种缓冲模式的更多信息:
我正在做一个学校项目(构建一个非常基础的 shell)。
我们的想法是能够像 bash 那样进行行版。为此,我将终端模式更改为非规范模式并停止回显。
我编写了一个非常简单的代码来暴露我的问题(请注意,我会检查函数 returns 等等......我只是为了这个 post 让它尽可能短)
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int ret;
char buff;
char *term_name;
char *termcap;
struct termios termios_new;
struct termios termios_backup;
/*
** Init termcap library
*/
term_name = getenv("TERM");
tgetent(NULL, term_name);
/*
** Get the terminal mode to non canonical and shut down echo
*/
bzero(&termios_new, sizeof(struct termios));
tcgetattr(STDIN_FILENO, &termios_backup);
termios_new = termios_backup;
termios_new.c_lflag &= ~(ICANON);
termios_new.c_lflag &= ~(ECHO);
termios_new.c_cc[VMIN] = 1;
termios_new.c_cc[VTIME] = 0;
/*
** Set the change
*/
tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_new);
/*
** Get the termcap for clearing screen on the particular terminal
*/
termcap = tgetstr("cl", NULL);
/*
** Loop read to get user entries and clear screen for 'c', output char for 'b', break for 'q'
*/
while((ret = read(STDIN_FILENO, &buff, 1)) > 0)
{
if (buff == 'c')
tputs(termcap, 1, putchar);
else if (buff == 'b')
putchar(buff);
else if (buff == 'q')
break ;
buff = 0;
}
/*
** Put back the terminal mode as found before
*/
tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_backup);
return (0);
}
所以基本上它是一个读取循环以捕获用户条目。它清除 'c' 的屏幕,'b' 的输出字符,中断并恢复 'q'.
的原始终端模式问题是:
每当我键入任何内容时,它似乎都被缓冲了,因为在我用 'q' 打破循环之前什么都没有发生。
此时,输出显示在屏幕上,如果我键入 5 次 b,我将得到 5 个 b,如果我键入 'c',屏幕将被清除。
但是,只有在输入 'q' 之后。
无论是否恢复原始终端模式,行为都是相同的。 (return
之前的最后一行)
我怀疑的是:
在使代码非常短并检查所有 returns 之后,我倾向于认为只能是我更改终端模式的方式有问题?
我尝试为 tcsetattr
函数使用标志 TCSAFLUSH
和 TCSADRAIN
,结果相同。
谢谢! :)
✅ 已解决:
好吧,对于遇到这种情况的任何人来说,这很有趣,因为它让我学到了很多东西(好吧,这是一个学校项目,所以...)。
在 tputs
参数中使用 putchar
是问题所在,因为 putchar 是缓冲的,就像 printf 一样。
我最终尝试了两件事,让我意识到:fflush()
在 tputs
函数调用起作用之后,显然问题与缓冲区相关。
然后尝试输出到 STDERR_FILENO,它也有点解决了,事实上 stderr 是唯一的 _IONBF
,所以没有缓冲。
所以我最终创建了一个只有 write
的 putchar 函数,仅此而已。
以下是有关三种缓冲模式的更多信息: