关于dup2()的疑问.

发表于:2007-05-26来源:作者:点击数: 标签:
#includestdio.h #includeunistd.h intmain(intargc,char*argv[]) { intfd; FILE*fp; //toopenalogfile if((fp=fopen("/var/log/ftp","w"))==NULL) { printf("fopenerror\n"); exit(1); } fd=fileno(fp); if(dup2(fd,STDOUT_FILENO)==-1){ fprintf(stderr,"R

#include<stdio.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
        int fd;
        FILE  *fp;

        //to open a log file
        if((fp=fopen("/var/log/ftp","w"))==NULL)
        {
                printf("fopen error \n");
                exit(1);
        }

        fd=fileno(fp);
        if(dup2(fd,STDOUT_FILENO)==-1){

                fprintf(stderr,"Redirect Standard Out error");
                exit(1);
        }
        printf("have a test\n");
        fclose(fp);
        printf("The end!\n");
}
[color=red:7399f1bclearcase/" target="_blank" >cc8][/color:7399f1bcc8]
为什么关掉文件后,还能把The end写进去?运行结果如下:
[root@localhost bxf]# ./a.out
[root@localhost bxf]# cat /var/log/ftp
have a test
The end!

 kj501 回复于:2004-02-05 18:32:46
fp是指向打开的/var/log/ftp,fclose(fp)也只是关闭这个文件。并没有关闭stdout。所以printf()仍然可以向stdout输出信息。由于stdout已经重定向为/var/log/ftp,信息自然输出到了/var/log/ftp中。

 flyingbxf 回复于:2004-02-16 13:09:44
再问一个问题:我把想要的信息重定向到文件后,便于进行分析判断下一步的操作。等将重要信息截取完后,我如果再想把输出重定向回来的话,应该用哪一个函数呢?不知道不退出程序的话,能不能重定向回来?

 lenovo 回复于:2004-02-16 13:45:35
你再close(fd)试试。

 flyingbxf 回复于:2004-02-16 15:46:02
:em09: 看见你我就感觉有戏了。马上去测试 :mrgreen:

 flyingbxf 回复于:2004-02-16 17:06:16
可惜。还是不行。

 kj501 回复于:2004-02-16 17:23:49
[quote:61173efb21="flyingbxf"]再问一个问题:我把想要的信息重定向到文件后,便于进行分析判断下一步的操作。等将重要信息截取完后,我如果再想把输出重定向回来的话,应该用哪一个函数呢?不知道不退出程序的话,能不能重定向回来?[/quote:61173efb21]
如果要把输出重定向回来,就应该在重定向前先保存stdout,然后在重定向完成之后,再把保存的stdout用dup2()恢复回来。

 flyingbxf 回复于:2004-02-16 20:13:57
[quote:f8afc85d0f="kj501"]
如果要把输出重定向回来,就应该在重定向前先保存stdout,然后在重定向完成之后,再把保存的stdout用dup2()恢复回来。[/quote:f8afc85d0f]
怎么个保存法?压栈?还是用另外的指针指向STDOUT?

 henngy 回复于:2004-02-17 16:03:51
用unlink试试

 flyingbxf 回复于:2004-02-17 16:37:06
unlink("/var/log/ftp");与 rm 命令 rm  /var/log/ftp 都是删除文件,其具体实现有什么区别吗?

 forest077 回复于:2004-02-17 18:31:38
dup2我没有用过,不过我知道freopen重定向输出到文件后如何恢复到终端上,不知和这个一样不一样。

 flyingbxf 回复于:2004-02-17 20:03:28
[quote:799918b9cb="forest077"]dup2我没有用过,不过我知道freopen重定向输出到文件后如何恢复到终端上,不知和这个一样不一样。[/quote:799918b9cb]
freopen怎么恢复??用fclose()吗?

 kj501 回复于:2004-02-17 21:24:40
[quote:18f546d406="flyingbxf"]
怎么个保存法?压栈?还是用另外的指针指向STDOUT?[/quote:18f546d406] 
int saved_fd ;
saved_fd = STDOUT_FILENO; /* 保存标准输出 */

dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */

 forest077 回复于:2004-02-17 21:44:12
freopen重定向后的恢复,基本原理是获得当前的tty,然后把输出流定向到当前tty即可,实现起来很简单,两三句话即可。范例如下:
//把stdout定向到文件aaa
fpout=freopen("aaa","w",stdout);
//现在printf会输出到文件aaa里面
fp=popen("tty","r");
fgets(str,sizeof(str),fp);
str[strlen(str)-1]=0;//当前tty保存在str里面了
fclose(fp);
//把fpout重定向到当前tty
freopen(str,"w",fpout);
//现在printf输出到当前终端
不知道这个功能适合不适合你。

 forest077 回复于:2004-02-17 21:45:11
kj501的方式我要试一试,如果可以,比我的方便多了。

 converse 回复于:2004-02-17 23:04:12
kj501的方法我试过了不行呀

 win_hate 回复于:2004-02-17 23:05:09
[quote:3c3d0dff64="flyingbxf"]alhost bxf]#为什么关掉文件后,还能把The end写进去?运行结果如下:
[root@localhost bxf]# ./a.out
[root@localhost bxf]# cat /var/log/ftp
have a test
The end![/quote:3c3d0dff64]

这是因为你 dup2 后, stdout 和 fp 都指向文件 /var/log/ftp, 关掉其中一个后,另一个仍然可写。

 win_hate 回复于:2004-02-17 23:08:32
[quote:e653c7e9b3="kj501"]
int saved_fd ;
saved_fd = STDOUT_FILENO; /* 保存标准输出 */

dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */[/quote:e653c7e9b3]

你写错了吧?这几行程序一般相当于:

int saved_fd;
save_fd = 2;
dup2 (2, 2);

这怎么行?

 win_hate 回复于:2004-02-17 23:12:18
这个估计行:

[code:1:f3e6cd9f61]
int sfd;
sfd = dup (STDOUT_FILENO);  /* save */
....

dup2 (sfd, STDOUT_FILENO); /* restore */

[/code:1:f3e6cd9f61]

 converse 回复于:2004-02-18 17:45:09
win_hate,你好!
如果我这么写:

int main(void)
{
        FILE *fp;
        int fd, id;
                                                                                
        id = dup(STDOUT_FILENO);
                                                                                
        fp = fopen("install.log", "w");
        if (fp == NULL) {
                printf("read error.\n");
                return 0;
        }
        fd = fileno(fp);
        dup2(fd, STDOUT_FILENO);
        printf("test\n");
        fclose(fp);
        printf("hello\n");
                                                                                
        dup2(id, STDOUT_FILENO);
        printf("world\n");
                                                                                
        return 0;
}

将在终端打印出
test
hello
world

如果我要做到在打开的文件install.log中写入
test
hello
而在终端输出
world
应该怎么作呢?

另外,在学习UNIX编程时我看的是APUE,感觉光看书中的例子还不够呀,那些例子功能比较单一,大多为了讲述一组函数的功能的,有没有那本书将大部分函数功能都用上,实现一个比较大的程序呢

 lenovo 回复于:2004-02-18 17:53:30
你的要求很奇怪哦。
那样的话你用fprintf()吧。
也没这么麻烦了。

 win_hate 回复于:2004-02-18 18:29:20
[quote:1e12026a55="converse"] dup2(id, STDOUT_FILENO);
printf("world\n");

return 0; [/quote:1e12026a55]

在 dup2 (id, STDOUT_FILENO); 之前加入

[code:1:1e12026a55]
fflush (stdout);
[/code:1:1e12026a55]

就能达到你要求的效果了。该死的缓冲.....

对另一个问题, 我认为只有写过 ''真正的程序'' 后才能融会贯通。如果没有参加实际项目的机会,可以多看看代码。

 converse 回复于:2004-02-18 21:55:22
我明白了,fflush函数把原来的test和hello强行写入打开的文件中,然后由于stdut被重新定位到屏幕上所以就在屏幕上输出world了,对吧??

我现在比较的郁闷,虽然找了一个作LINUX的公司,不过作的东西好象和我想像的有分别,暂时还用不上APUE里的知识,缺少了在实际中的锻炼学起来自然慢的多,我看了一下APUE后面有几个实际的例子,不过好象是数据库的,然后是打印机的,我这些都没有学过呀,不知道看过这本书的朋友感觉怎么样呢?

个人觉得APUE对于初学者来说太难了,很多基础的概念一带而过,还是lenovo推荐过的《UNIX程序设计教程》不错,把这两本书结合在一起看学的很快,就是可惜的是没有实践的锻炼,郁闷呀。。。。。。。。。

 lenovo 回复于:2004-02-18 22:35:39
[quote:3d57c6a105="converse"]我明白了,fflush函数把原来的test和hello强行写入打开的文件中,然后由于stdut被重新定位到屏幕上所以就在屏幕上输出world了,对吧??

我现在比较的郁闷,虽然找了一个作LINUX的公司,不过作的东西好象和我想像..........[/quote:3d57c6a105]
我看APUE看的也很慢,现在看到进程高级通信那里了,
时间也不是很充足。不过感觉用处还是很大的,
很多不明白的地方,那本书都说了。

 forest077 回复于:2004-02-19 09:45:19
说来惭愧,虽然我一直向别人大力推荐APUE,但是自己从来没有从头到尾看过一遍。我把它当作一本开发手册,用到时再去翻翻,呵呵。

 flyingbxf 回复于:2004-02-19 11:01:36
[quote="win_hate"]
在 dup2 (id, STDOUT_FILENO); 之前加入 

代码: 

[color=blue:5d83aef38c]fflush (stdout); [/color:5d83aef38c]
 

quote]

我没有加fflush (stdout); 也没有出现问题啊!为什么呢?我是在linux下运行的。

 win_hate 回复于:2004-02-19 11:41:07
[quote:06b22e07e5="flyingbxf"]
 

quote]

我没有加fflush (stdout); 也没有出现问题啊!为什么呢?我是在linux下运行的。[/quote:06b22e07e5]

我记得你是做嵌入式开发的,可能是环境不同。你换个台式机试一试可能就有这个问题。

 huahua0459 回复于:2004-02-19 18:32:59
dup2首先close标准输出,然后把标准输出的文件描述符1传到fd所代表的结构中?
close(fd)其实是close了一个没有用的数(3),fclose关闭的也是这个数字(3),可以通过fileno看到。所以还可以写道这个文件中。如果关闭了close(1),则不行了。
看来FILE和文件描述符最好不一起使用。不然一定要当心啊。

 kj501 回复于:2004-02-19 20:20:35
[quote:97d14ba4a1="win_hate"]

你写错了吧?这几行程序一般相当于:

int saved_fd;
save_fd = 2;
dup2 (2, 2);

这怎么行?[/quote:97d14ba4a1]
说错了,stdout应该是1,2是stderr。

 kj501 回复于:2004-02-19 20:42:05
[quote:dd9390adc3="kj501"]
int saved_fd ;
saved_fd = STDOUT_FILENO; /* 保存标准输出 */

dup2(saved_fd, STDOUT_FILENO); /* 恢复标准输出 */[/quote:dd9390adc3]
我来检讨一下,由于我对dup2的使用在概念有理解错误,我给出的例子是行不通的。
经过试验,我发现dup2(int oldfd, int newfd)其中的newfd必须是一个实际打开文件的文件描述符,不能只是一个不指向任何文件的整数。
这是我作试验的代码:
[code:1:dd9390adc3]
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>

int main()
{
int sfd,testfd;

testfd = open("temp",O_CREAT | O_RDWR | O_APPEND);

if (-1 == testfd) {
printf("open file error.\n");
exit(1);
}

/* 先复制一个真实的文件描述符 */
sfd = dup(testfd);

/* 保存标准输出 */
if (-1 == dup2(STDOUT_FILENO,sfd) ) {
printf("can't save fd \n");
exit(1);
}

/* 重定向 */
if (-1 == dup2(testfd,STDOUT_FILENO) ) {
printf("can't redirect fd error\n");
exit(1);
}

/* 此时向stdout写入应该输出到文件 */
write(STDOUT_FILENO,"file\n",5);

/* 恢复stdout */
if (-1 != dup2(sfd,STDOUT_FILENO) ) {
printf("recover fd ok \n");

/* 恢复后,写入stdout应该向屏幕输出 */
write(STDOUT_FILENO,"stdout\n",7);
}
}
[/code:1:dd9390adc3]
如果把其中的这一行注释掉:
[code:1:dd9390adc3]
sfd = dup(testfd);
[/code:1:dd9390adc3]
dup2函数执行时肯定会失败。
但从代码中也可以看出,我前面提出的先保存文件描述符,然后在重定向之后再恢复回来的思路是行得能的。
有不当之处,还请大家多多指教!

 win_hate 回复于:2004-02-19 20:48:40
[quote:2138f9c2b3="kj501"]
说错了,stdout应该是1,2是stderr。[/quote:2138f9c2b3]

说得对,我写错了, 应该是1

 win_hate 回复于:2004-02-19 21:16:51
[quote:b9495ae57a="kj501"]dup2函数执行时肯定会失败。
但从代码中也可以看出,我前面提出的先保存文件描述符,然后在重定向之后再恢复回来的思路是行得能的。
有不当之处,还请大家多多指教![/quote:b9495ae57a]

你对 dup2 的理解仍然有误, dup2 (a, b) 把 a 复制到 指定的“数字”(文件描述)上, 如果 b 是已打开的文件描述符, 先把 b 关掉(取消关联)。

你可以试一试, 把
[code:1:b9495ae57a]
sfd = dup(testfd); 
[/code:1:b9495ae57a]

注释掉,然后,很重要的一点,把 sfd 初始化为一个整数, 不要太大。我估计程序不会出错。

你试过后,请把结果告诉我。谢谢。

 flyingbxf 回复于:2004-02-20 09:20:16
[quote:300998a250="win_hate"]

我记得你是做嵌入式开发的,可能是环境不同。你换个台式机试一试可能就有这个问题。[/quote:300998a250]
我写错了,我的使用环境是uclinux,我在台式机上redhat8.0的环境下试了,不加fflush(stdout)果然全都输出到屏幕上了,文件里面没有写进去.不过我不明白能不能写进文件关缓冲什么事 呢?

 converse 回复于:2004-02-20 14:36:36
我来谈谈我的看法吧,我对这个问题还有一些疑问的
[code:1:d5193e31b0]
                                                                                                                                             #include <stdio.h>
#include <unistd.h>
                                                                                                                                              
int main(void)
{
        FILE *fp;
        int fd, id;
                                                                                                                                              
        id = dup(STDOUT_FILENO);//这时id和STDOUT_FILENO一起指向stdout
        if ((fp = fopen("install.log", "w")) == NULL) {
                perror("read error.\n");
                exit(-1);
        }
        fd = fileno(fp);//得到fp的文件描述符
        dup2(fd, STDOUT_FILENO);//STDOUT_FILENO和stdout流断开,             //STDOUT_FILENO指向fd的对应文件,这样使输出到达fd的文件
                                //fd对应的是已经打开的文件install.log,//而stdout在这里指的是终端,现在任何的输入都输入到
                                //install.log中
        printf("hello\n");
                                                                                                                                              
        fflush(stdout);//刷新stdout上的缓冲区,这样前面的hello写入文件//install.log中
        dup2(id, STDOUT_FILENO);
        printf("world\n");
        fclose(fp);
                                                                                                                                              
        return 0;
}

[/code:1:d5193e31b0]

以上的注释基本表达了我对这几个函数运用的了解,不过这里对fflush(stdout)的运用还有一点疑问,fflush(stream)刷新的是流stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?按理来说,文件描述符比流更加底层才对呀

 converse 回复于:2004-02-20 14:39:21
倒,我的代码怎么在这里变得这么乱呀

 win_hate 回复于:2004-02-20 22:32:39
[quote:3ecf7dbf24="converse"]
fflush(stdout);//刷新stdout上的缓冲区,这样前面的hello写入文件 install.log中
        dup2(id, STDOUT_FILENO);
        printf("world\n");
        fclose(fp); [/quote:3ecf7dbf24]

[quote:3ecf7dbf24="converse"]
不过这里对fflush(stdout)的运用还有一点疑问,fflush(stream)刷新的是流 stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?按理来说,文件描述符比流更加底层才对呀[/quote:3ecf7dbf24]

呵呵, 确实如此。 事实上,文件指针,比如说fp, 是根据fileno(fp)来对应文件的。你看上面的代码,STDOUT_FILENO 已经对应到 install.log,所以,当fflush(stdout)后, 缓冲内容写入文件 install.log。如果不fflush,执行
dup2 (id, STDOUT_FILENO), 这时 STDOUT_FILENO又对应回屏幕了, 然后缓冲中的内容和后来的数据都输到屏幕上了。

 kj501 回复于:2004-02-21 10:56:37
[quote:4624443cae="win_hate"]
注释掉,然后,很重要的一点,把 sfd 初始化为一个整数, 不要太大。我估计程序不会出错。

你试过后,请把结果告诉我。谢谢。[/quote:4624443cae]
你的说法一点不错,我将
[code:1:4624443cae]
sfd = dup(testfd);
[/code:1:4624443cae]
注释掉后,再将sfd初始化为0~1023之间的任何数,都可以执行成功。看来问题出在这一句:
[code:1:4624443cae]
dup2(STDOUT_FILENO,sfd);
[/code:1:4624443cae]
我用的系统是linux(内核2.4.21),由于linux内核限制最大打开的文件总数为1024(这一点可能改进了,原来很多书上说的是最大只能打开256个文件),因此,dup2在执行时肯定会进行文件描述符的合法性检查,如果大于1023,肯定导致失败。sfd没有初始化时的值是由系统任意给出的(将sfd没有初始化的值打印出来,结果是1073817472),已经超出了1023的限制,从而导致dup2()执行失败。这才是问题的真正原因。其实在man文档中对错误原因说得很清楚:“oldfd  isn't  an  open  file  descriptor, or newfd is out of the allowed range for file descriptors.” 只是我自己没有认真看。
经过这次讨论,感觉自己对dup2()的理解加深不少,APUE上对dup2()的介绍对于全面理解这两个函数的使用并不充分。以后man文档一定要仔细看。
最后,非常感谢win_hate,希望以后能和你多多交流。:)

 converse 回复于:2004-02-21 11:22:35
这样针对一组函数功能的讨论真的可以提高很快,我也受益匪浅呀

建议斑竹加为精华

 win_hate 回复于:2004-02-21 12:50:17
[quote:a687efc748="kj501"]最后,非常感谢win_hate,希望以后能和你多多交流。[/quote:a687efc748]

kj501 兄客气了, 这里我常来,期待与大家交流,向各位学习。

 forest077 回复于:2004-02-21 16:02:53
我有一个不解的地方,就是freopen实现的功能也是把一个流重定向到另一个流去,它和dup、dup2的底层实现有什么不一样吗?

 win_hate 回复于:2004-02-21 23:05:43
[quote:4f427b3b10="forest077"]我有一个不解的地方,就是freopen实现的功能也是把一个流重定向到另一个流去,它和dup、dup2的底层实现有什么不一样吗?[/quote:4f427b3b10]

freopen 并不是把一个流定向到另一个流,而是把一个流对应到一个新的文件:

fd1 = dup (fd);   fd1, fd 对应同一个文件, 但不能指定 fd1 的值。

dup2 (fd, fd1);  fd1, fd 对应同一个文件,但可以指定 fd1的值,根据 kj501提供的资料, fd1 不能大于 1024

freopen ("xxx", "w", fp);  关掉 fp 对应的文件, 并打开文件 xxx, 指针 fp 的值不变。

在前两种情况中, 都有两个文件描述符出现, 但在第三种情况中, 只有一个文件指针。

使用 freopen (..., fp) 时, 原来 fp 对应的缓存写入, 关掉 fileno(fp), 寻找一个最小未用的文件描述符, 在其上打开新文件。也就是说, fp 值不变, 其指向的 FILE 结构仍然使用, 但 fileno (fp) 则有可能改变。

 Wangwen 回复于:2004-02-21 23:42:35
[quote:0f345bc08a="win_hate"]

呵呵, 确实如此。 事实上,文件指针,比如说fp, 是根据fileno(fp)来对应文件的。你看上面的代码,STDOUT_FILENO 已经对应到 install.log,所以,当fflush(stdout)后, 缓冲内容写入文件 install.log。如果不fflus..........[/quote:0f345bc08a]
可以理解为 dup2首先close标准输出,然后把标准输出的文件描述符STDOUT_FILENO 传到fd所代表的fp文件指针结构中

STDOUT_FILENO 指向install.log的文件表

文件指针stdout的文件描述符为STDOUT_FILENO

所以fflush(stdout)后, 缓冲内容写入文件 install.log

 win_hate 回复于:2004-02-22 08:48:06
[quote:3af0064132="Wangwen"]
可以理解为 dup2首先close标准输出,然后把标准输出的文件描述符STDOUT_FILENO 传到fd所代表的fp文件指针结构中

STDOUT_FILENO 指向install.log的文件表

文件指针stdout的文件描述符为STDOUT_FILENO

所以f..........[/quote:3af0064132]

说法有误,dup2 是低级 I/O 操作函数, 不会直接与 fp 指向的文件结构打交道。

 flyingbxf 回复于:2004-02-23 09:24:54
[quote:3716d38f75="converse"]
fflush(stream)刷新的是流stream上的缓冲,是不是可以理解为不论文件描述符STDOUT_FILENO定位到了哪个文件,stdout都保存有未写入文件的缓冲区的内容呢?........[/quote:3716d38f75]
不用fflush(stream)就写不进文件,但为什么不用fflush(stream)就能输出到屏幕上呢?

 flyingbxf 回复于:2004-02-23 09:29:40
还有就是  dup2(STDOUT_FILENO,sfd); 和 
 dup2(sfd,STDOUT_FILENO); 起的作用是一样的?都是将两个文件描述符指向同一个文件是吗?

 Wangwen 回复于:2004-02-23 12:08:53
可以理解为 dup2首先close标准输出,

将STDOUT_FILENO 指向install.log的文件表 

文件指针stdout的文件描述符为STDOUT_FILENO 

所以fflush(stdout)后, 缓冲内容写入文件 install.log

使用fprintf(fp,"hello\n");fflush(fp);仍然可以将hello写入install.log

dup2(id, STDOUT_FILENO)使STDOUT_FILENO指向标准输出

printf("world\n");写入终端

 Wangwen 回复于:2004-02-23 12:11:24
[quote:8fb1668b95="flyingbxf"]
不用fflush(stream)就写不进文件,但为什么不用fflush(stream)就能输出到屏幕上呢?[/quote:8fb1668b95]
程序结束是 执行了fflush(stream)

 win_hate 回复于:2004-02-23 14:14:13
[quote:40572341d5="Wangwen"]可以理解为 dup2首先close标准输出,

将STDOUT_FILENO 指向install.log的文件表

文件指针stdout的文件描述符为STDOUT_FILENO

所以fflush(stdout)后, 缓冲内容写入文件 install.log

使用fprintf(fp,"hello\n");fflush(fp);仍然可以将hello写入install.log

dup2(id, STDOUT_FILENO)使STDOUT_FILENO指向标准输出

printf("world\n");写入终端[/quote:40572341d5]

Great!

 Wangwen 回复于:2004-02-23 14:25:49
xiexie

 converse 回复于:2004-02-23 20:10:51
茅塞顿开,不错不错,强烈建议加精

原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)