鱼C论坛

 找回密码
 立即注册
123
返回列表 发新帖
楼主: 1613551

[已解决]有人可以帮我分析一下小甲鱼的程序吗?

[复制链接]
发表于 2022-4-12 15:53:34 | 显示全部楼层
1613551 发表于 2022-4-12 15:32
对对对,就是这个意思

这个问题我以前也想过,确实是个有意思的论题
一会儿我写个笔记帖,然后把链接发过来
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

好的!!!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-12 17:29:35 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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' 是不会输出的

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

  3. int main(void) {
  4.     printf("hello world!");
  5.     sleep(5);
  6.     printf("\n");
  7.     return 0;
  8. }
复制代码


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

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

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

  2. int main(void) {
  3.     setvbuf(stdin, NULL, _IONBF, 0);
  4.     int ch;
  5.     while((ch = getchar()) != 'q') putchar(ch);
  6.     return 0;
  7. }
复制代码


你试一下这个程序会发现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/

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


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

  5. int main(void) {
  6.     struct termios termios;
  7.     ioctl(STDIN_FILENO, TCGETS, &termios);
  8.     termios.c_lflag &= ~ICANON;
  9.     ioctl(STDIN_FILENO, TCSETS, &termios);
  10.     setvbuf(stdin, NULL, _IONBF, 0);
  11.     int ch;
  12.     while((ch = getchar()) != 'q') putchar(ch);
  13.     return 0;
  14. }
复制代码


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

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

  5. int main(void) {
  6.     struct termios termios;
  7.     ioctl(STDIN_FILENO, TCGETS, &termios);
  8.     termios.c_lflag &= ~ICANON;
  9.     ioctl(STDIN_FILENO, TCSETS, &termios);
  10.     //setvbuf(stdin, NULL, _IONBF, 0);
  11.     int ch;
  12.     while((ch = getchar()) != 'q') putchar(ch);
  13.     return 0;
  14. }
复制代码


改成这样也还是 输入一个字符就读取一个字符,就显示一个字符
所以 stdio 库中的这个 setvbuf 究竟有什么用?
我无法回答这个问题,不管这个了
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-12 23:58:22 | 显示全部楼层
setvbuf 这个函数还挺有意思的
下面这个代码是行缓存的(强制设置成行缓存),依然是 输入一个字符就读取一个字符,就显示一个字符
意思就是无法设置成行缓存模式

  1. #include <stdio.h>
  2. #include <sys/ioctl.h>
  3. #include <unistd.h>
  4. #include <termios.h>

  5. int main(void) {
  6.     struct termios termios;
  7.     ioctl(STDIN_FILENO, TCGETS, &termios);
  8.     termios.c_lflag &= ~ICANON;
  9.     ioctl(STDIN_FILENO, TCSETS, &termios);
  10.     setvbuf(stdin, NULL, _IOLBF, 1024);
  11.     int ch;
  12.     while((ch = getchar()) != 'q') putchar(ch);
  13.     return 0;
  14. }
复制代码


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

  5. int main(void) {
  6.     struct termios termios;
  7.     ioctl(STDIN_FILENO, TCGETS, &termios);
  8.     termios.c_lflag &= ~ICANON;
  9.     ioctl(STDIN_FILENO, TCSETS, &termios);
  10.     setvbuf(stdin, NULL, _IOFBF, 1024);
  11.     int ch;
  12.     while((ch = getchar()) != 'q') putchar(ch);
  13.     return 0;
  14. }
复制代码


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


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

  1. #include <stdio.h>
  2. #include <sys/ioctl.h>
  3. #include <unistd.h>
  4. #include <termios.h>

  5. int main(void) {
  6.     /*
  7.     struct termios termios;
  8.     ioctl(STDIN_FILENO, TCGETS, &termios);
  9.     termios.c_lflag &= ~ICANON;
  10.     ioctl(STDIN_FILENO, TCSETS, &termios);
  11.     */
  12.     setvbuf(stdin, NULL, _IOFBF, 1024);
  13.     int ch;
  14.     while((ch = getchar()) != 'q') putchar(ch);
  15.     return 0;
  16. }
复制代码


不玩了,到此为止
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-13 00:01:27 | 显示全部楼层
“但是输入字符q,程序就直接结束了,并不需要按下回车键了”
仔细想了想,因为设置成全缓存了,并不是行缓存,全缓存不需要 '\n'
所以,“结果真就全缓存了(大概)” 可以去掉 “大概” 了
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-13 00:10:06 | 显示全部楼层
嗯,有一个细节忘记说了

  1. 无缓存:不对IO操作进行缓存,对流的读写可以立即操作实际文件。典型例子就是标准出错
复制代码

这里的意思是说 stderr 默认是 无缓存的?
但是
  1. 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.
复制代码


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


意思就是 反正不是全缓存,行缓存和无缓存 由系统的设计者二选一
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-13 00:13:03 | 显示全部楼层
  1. 因为stdin默认是行缓存的,getchar使用的就是stdin
  2. 看下面这个程序,这个程序是在5秒后才输出的 hello world!
  3. 因为stdout没有看到 '\n' 是不会输出的
复制代码

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


这么说是错误的
应该是 行缓存和无缓存 由系统的设计者二选一
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-13 13:58:03 | 显示全部楼层
人造人 发表于 2022-4-13 00:13
这么说是错误的
应该是 行缓存和无缓存 由系统的设计者二选一
^_^

这....大佬辛苦了,你这讲得算是特别彻底了,总算是解决了我学了这么久以来的疑惑
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-8-10 11:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表