stdin/stdout/stderr的缓冲方式疑问

sh/bash/dash/ksh/zsh等Shell脚本
回复
头像
jiandan23
帖子: 94
注册时间: 2010-12-17 22:31
系统: Mint 19.2

stdin/stdout/stderr的缓冲方式疑问

#1

帖子 jiandan23 » 2015-03-06 14:07

从网上下载了了程序,用来判断stdin、stdout、stderr的缓冲方式。
照道理来说,stdin/stdout应该是行缓冲,stderr是无缓冲。
但是我的程序输出如下:
捕获.JPG
捕获.JPG (29.8 KiB) 查看 3798 次
这个是不是有问题啊?

程序如下:
#include <stdio.h>
int main(void)
{
printf("stdin is ");
if(stdin->_flags & _IO_UNBUFFERED) /* 判断标准输入流对象的缓冲区类型*/
printf("unbuffered\n");
else if(stdin->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stdin->_IO_buf_end - stdin->_IO_buf_base); /* 输出缓冲区的大小 */
printf("discriptor is %d\n\n", fileno(stdin)); /* 输出文件描述符 */

printf("stdout is ");
if(stdout->_flags & _IO_UNBUFFERED) /* 判断标准输出流对象的缓冲区类型*/
printf("unbuffered\n");
else if(stdout->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stdout->_IO_buf_end - stdout->_IO_buf_base); /* 输出缓冲区的大小 */
printf("discriptor is %d\n\n", fileno(stdout)); /* 输出文件描述符 */

printf("stderr is ");
if(stderr->_flags & _IO_UNBUFFERED) /* 判断标准出错流对象的缓冲区类型*/
printf("unbuffered\n");
else if(stderr->_flags & _IO_LINE_BUF)
printf("line-buffered\n");
else
printf("fully-buffered\n");
printf("buffer size is %d\n", stderr->_IO_buf_end - stderr->_IO_buf_base); /* 输出缓冲区的大小 */
printf("discriptor is %d\n\n", fileno(stderr)); /* 输出文件描述符 */
return 0;
}
infidel
帖子: 136
注册时间: 2012-03-28 10:16

Re: stdin/stdout/stderr的缓冲方式疑问

#2

帖子 infidel » 2015-03-06 15:30

记得Unix环境高级编程里面有说过这个问题。
rosynirvana
帖子: 893
注册时间: 2011-02-14 17:46

Re: stdin/stdout/stderr的缓冲方式疑问

#3

帖子 rosynirvana » 2015-03-06 15:30

1. ISO C标准: 当且仅当stdio和stdout被定向到交互式设备(例如终端)时,这两个FILE是行缓冲。stderr不可以是全缓冲
2. FILE结构体内部是私有的(当然C在语法上面限制不了),试图读取和改变其内部变量的行为都是不可预期的

这段程序对于glibc其实是有效的,例如在最前面加个getchar(); 然后输出的就是你预期的结果了
因为glibc的实现认为没有任何输入的时候,stdio没有被重定向到一个交互式设备(虽然fd是0)
infidel
帖子: 136
注册时间: 2012-03-28 10:16

Re: stdin/stdout/stderr的缓冲方式疑问

#4

帖子 infidel » 2015-03-06 15:40

Unix环境高级编程第三版,P174页。
标准io带缓冲,具体带什么样的缓冲要看标准io连接到什么设备上。
如果终端设备行缓冲,否则全缓冲。

书上这么写的,但是具体的系统可能不一样。
可以看看上面书本的125页。
5.12中描述的很详细。
infidel
帖子: 136
注册时间: 2012-03-28 10:16

Re: stdin/stdout/stderr的缓冲方式疑问

#5

帖子 infidel » 2015-03-06 15:52

orig.c是原来的程序
aa.c是连接到终端,getchar从终端读取输入。

root@umn:~/test# diff orig.c aa.c -U 4
--- orig.c 2015-03-06 02:51:26.318490643 -0500
+++ aa.c 2015-03-06 02:49:31.153442763 -0500
@@ -1,7 +1,9 @@
#include <stdio.h>
+
int main(void)
{
+ getchar();
printf("stdin is ");
if(stdin->_flags & _IO_UNBUFFERED) /* 判断标准输入流对象的缓冲区类型*/
printf("unbuffered\n");
else if(stdin->_flags & _IO_LINE_BUF)

编译之。

root@umn:~/test# gcc aa.c
aa.c: In function ‘main’:
aa.c:13:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf("buffer size is %d\n", stdin->_IO_buf_end - stdin->_IO_buf_base); /* 输出缓冲区的大小 */
^
aa.c:23:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf("buffer size is %d\n", stdout->_IO_buf_end - stdout->_IO_buf_base); /* 输出缓冲区的大小 */
^
aa.c:33:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf("buffer size is %d\n", stderr->_IO_buf_end - stderr->_IO_buf_base); /* 输出缓冲区的大小 */
^

---
test case:
1. 使用终端输入,检查程序输出:

root@umn:~/test# ./a.out
input <-- input手动输入
stdin is line-buffered --> line buffered.
buffer size is 1024
discriptor is 0

stdout is line-buffered
buffer size is 1024
discriptor is 1

stderr is unbuffered
buffer size is 0
discriptor is 2

2. 使用定向,stdin连接到文件上。

root@umn:~/test# echo input > input
root@umn:~/test# ./a.out < input --> 连接到文件
stdin is fully-buffered --> fully-buffered.
buffer size is 4096
discriptor is 0

stdout is line-buffered
buffer size is 1024
discriptor is 1

stderr is unbuffered
buffer size is 0
discriptor is 2

这个例子应该很清楚说明你的问题了。


append我的环境
root@umn:~/test# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.2 LTS --> ubuntu 14.04.2
Release: 14.04
Codename: trusty
头像
jiandan23
帖子: 94
注册时间: 2010-12-17 22:31
系统: Mint 19.2

Re: stdin/stdout/stderr的缓冲方式疑问

#6

帖子 jiandan23 » 2015-03-06 17:48

谢谢楼上各位的回复!
再次确认一下:意思是说,只有当进程产生输入输出之后,文件缓冲的类型才被确认。
rosynirvana
帖子: 893
注册时间: 2011-02-14 17:46

Re: stdin/stdout/stderr的缓冲方式疑问

#7

帖子 rosynirvana » 2015-03-06 19:23

应该说是用了C标准库的函数后
可以再试验一下,检测stdin,getchar(); 检测stdin,然后就会发现前一个是全缓冲,后一个是行缓冲
再把getchar改成read,会发现两个都是全缓冲
回复