read changes stdout from unbuffered to line buffered in canonical mode

点点圈 提交于 2019-12-23 02:36:38

问题


When I use this piece of code in canonical mode:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

static struct termios newt;
static struct termios oldt;

static void kb_fini(void)
{
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}

void kb_init(void)
{
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= (tcflag_t)~(ICANON | ECHO | ISIG);
    newt.c_cc[VMIN] = 1;
    newt.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    atexit(kb_fini);
}

int main(void)
{
    int c;

    kb_init();
    printf("Press q ");
    c = getchar();
    if (c == 'q') {
        printf("q was pressed\n");
    }
    return 0;
}

I am able to read "Press q " in console before pressing q

Switching to read:

int main(void)
{
    char c;

    kb_init();
    printf("Press q ");
    read(STDIN_FILENO, &c, 1);
    if (c == 'q') {
        printf("q was pressed\n");
    }
    return 0;
}

Doesn't show "Press q " until q is pressed.

Why is this?


回答1:


As I observed in a comment, the standard I/O package knows what is happening and coordinates things so that the pending output to standard output (stdout) is flushed before invoking the read operation on standard input (stdin) — at least when the output and input are an 'interactive device', aka terminal. Note that the synchronization is not actually mandated by the C standard, but most implementations provide it.

The read() system call doesn't know or care about what's going on with the standard I/O package. It has no access to any of the file streams, nor to any of the data private to those streams (such as buffered output). Therefore, it cannot ensure that pending standard output is flushed before it attempts to read the input.

If you're going to mix the two modes, make sure you fflush(stdout); or fflush(0); before using read().

Does mixing the two modes have well defined behaviour?

It depends on how you mix them. If you use stdout for output and STDIN_FILENO for input, there's no problem beyond the lack of synchronization by default. If you try to mix stdout operations with operations directly on STDOUT_FILENO, or stdin operations with operations directly on STDIN_FILENO, then you are in for a world of hurt, in general. Don't try that as you value your own (or your users') sanity. Amongst other problems, the standard I/O library can buffer ahead and the file descriptor functions won't be able to see what standard I/O already read. Conversely, on writing, the standard I/O library will buffer and the file descriptor I/O won't.



来源:https://stackoverflow.com/questions/34241929/read-changes-stdout-from-unbuffered-to-line-buffered-in-canonical-mode

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!