风车呼呼呼 发表于 2022-4-12 15:53:34

1613551 发表于 2022-4-12 15:32
对对对,就是这个意思

这个问题我以前也想过,确实是个有意思的论题
一会儿我写个笔记帖,然后把链接发过来

1613551 发表于 2022-4-12 17:16:51

风车呼呼呼 发表于 2022-4-12 15:53
这个问题我以前也想过,确实是个有意思的论题
一会儿我写个笔记帖,然后把链接发过来

好的!!!

风车呼呼呼 发表于 2022-4-12 17:29:35

1613551 发表于 2022-4-12 17:16
好的!!!

https://fishc.com.cn/thread-211847-1-1.html

人造人 发表于 2022-4-12 23:44:36

1613551 发表于 2022-4-12 15:32
对对对,就是这个意思

什么是全缓存、行缓存、无缓存?
https://zhuanlan.zhihu.com/p/341280434

因为stdin默认是行缓存的,getchar使用的就是stdin
看下面这个程序,这个程序是在5秒后才输出的 hello world!
因为stdout没有看到 '\n' 是不会输出的

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

int main(void) {
    printf("hello world!");
    sleep(5);
    printf("\n");
    return 0;
}


stdin也一样,默认是行缓存的,没有看到 '\n' 就一直阻塞,直到发现 '\n'

但是,这里有但是
但是有一个函数可以改变这个默认的行为
setvbuf
http://www.cplusplus.com/reference/cstdio/setvbuf/

看下面的程序
#include <stdio.h>

int main(void) {
    setvbuf(stdin, NULL, _IONBF, 0);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


你试一下这个程序会发现getchar依然等待 '\n'
我通过百度期望找到原因
http://www.dovov.com/setvbufstdin.html
这篇文章看起来是机翻,翻的某篇英文文章
大概的意思是说,虽然stdio这个库已经不使用行缓存了,但是终端还是行缓存的
那么继续百度,然后改代码

百度得到的结果是,要关闭这个行缓存,需要把终端切换成原始模式
https://blog.csdn.net/zhizhengguan/article/details/117788098

使用的函数是 tcgetattr/tcsetattr
但是我记得这类函数是封装了一些底层函数,我忘了是哪个函数了,一开始我猜的是 fcntl 函数,但是不对
通过百度,我找到了这个函数,是 ioctl 函数
为什么不使用 tcgetattr/tcsetattr ? 因为这些函数是对一系列ioctl函数的封装,为了学习原理,就不能用这些封装好的函数,一个函数所有的事情都解决了,问题是我想知道这个函数背后做了什么,所以我选择不使用这些封装好的函数
我选择使用ioctl函数
我在这个函数的文档里面没有找到切原始模式的参数
通过百度找到了这个 TCGETS/TCSETS
https://www.bbsmax.com/A/Gkz1k7Qq5R/

大部分termios 库函数会被转化为对tty 设备节点的ioctl()调用,例如tcgetattr() 、 tcsetattr() 函数对应着TCGETS、TCSETS IO 控制命令。

然后把代码改成这样
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    struct termios termios;
    ioctl(STDIN_FILENO, TCGETS, &termios);
    termios.c_lflag &= ~ICANON;
    ioctl(STDIN_FILENO, TCSETS, &termios);
    setvbuf(stdin, NULL, _IONBF, 0);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


这样确实是可以了,输入一个字符就读取一个字符,就显示一个字符

但是我还有一个问题
“大概的意思是说,虽然stdio这个库已经不使用行缓存了,但是终端还是行缓存的”
那终端不是行缓存的,stdio这个库是行缓存的,getchar会是怎样的行为?
为了测试这个问题
代码改成这样
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    struct termios termios;
    ioctl(STDIN_FILENO, TCGETS, &termios);
    termios.c_lflag &= ~ICANON;
    ioctl(STDIN_FILENO, TCSETS, &termios);
    //setvbuf(stdin, NULL, _IONBF, 0);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


改成这样也还是 输入一个字符就读取一个字符,就显示一个字符
所以 stdio 库中的这个 setvbuf 究竟有什么用?
我无法回答这个问题,不管这个了
^_^

人造人 发表于 2022-4-12 23:58:22

setvbuf 这个函数还挺有意思的
下面这个代码是行缓存的(强制设置成行缓存),依然是 输入一个字符就读取一个字符,就显示一个字符
意思就是无法设置成行缓存模式

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    struct termios termios;
    ioctl(STDIN_FILENO, TCGETS, &termios);
    termios.c_lflag &= ~ICANON;
    ioctl(STDIN_FILENO, TCSETS, &termios);
    setvbuf(stdin, NULL, _IOLBF, 1024);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


下面这个代码设置成全缓存的
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    struct termios termios;
    ioctl(STDIN_FILENO, TCGETS, &termios);
    termios.c_lflag &= ~ICANON;
    ioctl(STDIN_FILENO, TCSETS, &termios);
    setvbuf(stdin, NULL, _IOFBF, 1024);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


结果真就全缓存了(大概)
因为输入一个字符,不在输出一个字符
但是输入字符q,程序就直接结束了,并不需要按下回车键了


代码改成这样,程序的行为和上面一个代码一样,所以 “结果真就全缓存了(大概)” 这个“大概”可以去掉了,(大概)
^_^

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    /*
    struct termios termios;
    ioctl(STDIN_FILENO, TCGETS, &termios);
    termios.c_lflag &= ~ICANON;
    ioctl(STDIN_FILENO, TCSETS, &termios);
    */
    setvbuf(stdin, NULL, _IOFBF, 1024);
    int ch;
    while((ch = getchar()) != 'q') putchar(ch);
    return 0;
}


不玩了,到此为止

人造人 发表于 2022-4-13 00:01:27

“但是输入字符q,程序就直接结束了,并不需要按下回车键了”
仔细想了想,因为设置成全缓存了,并不是行缓存,全缓存不需要 '\n'
所以,“结果真就全缓存了(大概)” 可以去掉 “大概” 了
^_^

人造人 发表于 2022-4-13 00:10:06

嗯,有一个细节忘记说了

无缓存:不对IO操作进行缓存,对流的读写可以立即操作实际文件。典型例子就是标准出错
这里的意思是说 stderr 默认是 无缓存的?
但是
The default streams stdin and stdout are fully buffered by default if they are known to not refer to an interactive device. Otherwise, they may either be line buffered or unbuffered by default, depending on the system and library implementation. The same is true for stderr, which is always either line buffered or unbuffered by default.

有道翻译
默认流stdin和stdout在默认情况下是完全缓冲的,如果已知它们不引用交互式设备。否则,默认情况下,它们可能是行缓冲的,也可能是非缓冲的,这取决于系统和库的实现。对于stderr也是如此,默认情况下,它总是行缓冲或非缓冲。

意思就是 反正不是全缓存,行缓存和无缓存 由系统的设计者二选一

人造人 发表于 2022-4-13 00:13:03

因为stdin默认是行缓存的,getchar使用的就是stdin
看下面这个程序,这个程序是在5秒后才输出的 hello world!
因为stdout没有看到 '\n' 是不会输出的

stdin也一样,默认是行缓存的,没有看到 '\n' 就一直阻塞,直到发现 '\n'

这么说是错误的
应该是 行缓存和无缓存 由系统的设计者二选一
^_^

1613551 发表于 2022-4-13 13:58:03

人造人 发表于 2022-4-13 00:13
这么说是错误的
应该是 行缓存和无缓存 由系统的设计者二选一
^_^

这....大佬辛苦了,你这讲得算是特别彻底了,总算是解决了我学了这么久以来的疑惑
页: 1 2 [3]
查看完整版本: 有人可以帮我分析一下小甲鱼的程序吗?