标准输出重定向到文件和printf函数

qq_33487700 2020-07-29 03:48:53
1. linux下用dup2关闭标准输出后,使用printf打印信息的问题。
2. 代码如下:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main()
{
int fd1=open("eup.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if(fd1==-1){
printf("#LINE %d:",__LINE__);
perror("open");
return -1;
}
//printf("Hello, World!\n");//不加这句,eup.txt中没内容。
int fd2=dup2(fd1,1);
if(fd2==-1){
printf("#LINE %d:",__LINE__);
perror("open");
return -1;
}
printf("fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3);
close(fd2);
close(fd1);
return 0;
}
3.问题为:将printf("Hello, World!\n");这句话注释掉,运行程序后eup.txt里面
没有任何内容,这个是正确的。但取消注释后,运行程序,发现eup.txt里面
有如下内容:
fd1:4
fd2:1
fd3:3
这是为什么?哪个函数让刷新了文件缓冲区??
首先\n不能刷新文件缓冲区的。如果是close函数刷新的,为什么注释printf("Hello, World!\n");
这句话时运行程序后没有将内容刷新到 eup.txt文件中??
哪位大神知道??


...全文
13095 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
alittlenewbiek 2020-08-03
  • 打赏
  • 举报
回复
Good!我顺着你的思路找到了这篇内容 -> 前往stackoverflow
你应该也能看明白,我还是说一下我的理解。

无论是C/C++,它的输入输出函数并不会负责line-buffered还是full-buffered,这个由输出的文件属性来决定;
如果是interactive device,那么就是line-buffered;
否则,就是full-buffered。

但是C/C++怎么判断输出文件的属性呢?文中也说了,不同的实现尽管不同,但差不多都是用stat();
并且一旦决定了一种缓存方式,后面就不会改变了(除非显式的setvbuf())

所以,先printf的时候,C已经明确它的输出stdout导向terminal tty,所以使用line-buffered的方式,
但后来用dup2()重定向,但这对于C来说是不知道的,依然用line-buffered的方式刷新它的缓存区。

然而一开始就把stdout重定向到file的话,C是可以了解到这一信息并改变成full-buffered的方式。
qq_33487700 2020-08-03
  • 打赏
  • 举报
回复
是我问的。我就是想知道在不注释 printf("Hello, World!\n"); 的前提下运行程序,为什么 printf("fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3); 能够把内容写入eup.txt 。

后来看了一些资料说:printf向终端或者屏幕输出时是行缓冲(\n 可以刷缓冲区),向文件输出时是全缓冲(缓冲区满打印,一般缓冲区不是几个字符能充满的)。

注释掉 printf("Hello, World!\n"); 后,由于 int fd2=dup2(fd1,1); 使得标准输出重定向到文件,printf是全缓冲,所以printf("fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3); 中 \n 并不能刷缓冲区,数据只是在缓冲区,并没有写入文件里。

不注释 printf("Hello, World!\n"); ,即使后面调用了 int fd2=dup2(fd1,1); printf还是行缓冲,并没有变为全缓冲(原因我也不知道,只是听别人说的)。所以 printf("fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3); 中 \n 能刷缓冲区,数据写入了文件里。
alittlenewbiek 2020-08-01
  • 打赏
  • 举报
回复
cplusplus forum里的应该也是你吧,不知道你是从哪里看的这个问题,有点厉害。
因为这个问题,学习了一些函数,感谢!
close()并不能刷新缓冲区,这个在man close里有明确讲到,并且也提到使用fsync;但是fsync对tty无效,所以也不行。
引用
A successful close does not guarantee that the data has been successfully saved to
disk, as the kernel defers writes. It is not common for a filesystem to flush the
buffers when the stream is closed. If you need to be sure that the data is physically
stored, use fsync(2).

经过尝试,有3种方法可以写入eup.txt。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#define METHOD 1

int main()
{
#if METHOD == 1
setvbuf(stdout, NULL, _IONBF, 0);
#endif
int fd1=open("eup.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if(fd1==-1){
printf("#LINE %d:",__LINE__);
perror("open");
return -1;
}
/* printf("Hello, World!\n"); */
int fd2=dup2(fd1,1);
if(fd2==-1){
printf("#LINE %d:",__LINE__);
perror("open");
return -1;
}
printf("fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3);

#if METHOD == 2
char buf[200] = {0};
sprintf(buf, "fd1:%d\nfd2:%d\nfd3:%d\n",fd1,fd2,3);
write(fileno(stdout), buf, strlen(buf)+1);
#endif

#if METHOD == 3
fclose(stdout);
#endif
close(fd2);
close(fd1);
return 0;
}

1. 把stdout改成无缓冲,说明和缓冲方式有很大关系吧(?)
2. 用write
3. close(fd2) -> fclose(stdout),fclose为何神奇,看了一下glibc/libio/iofclose.c里面_IO_new_fclose的源码,表示看不懂...

综上,这个问题与Linux/Unix系统内核可能更相关;常用标准C库函数。

23,118

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧