dazuiniu's blog

cat /dev/dazuiniu/random

Archive for the ‘dup2’ tag

pipe编程

View Comments

管道这个概念在 unix 系的系统上占有了相当大的份量。如果我们需要手动编程实现这样的功能,那么我们可以救助于 pipe 这个系统调用。

这个系统调用的输入是一个数组,存放到时候可以使用的两个文件描述符,大嘴牛在这里使用pipes[0] 和 pipes[1] 来分别表示。

当系统调用返回的时候,这两个文件描述符就可以使用了,其中 pipes[0] 用于读,而 pipes[1] 用于写,这就正好形成了一个管道,在写入的数据都会出现在读的那端。

注意这个管道是单向的,不像 tcp 连接那样双向都可以读写数据。

当两个文件描述符都被关闭的时候,那么这个管道也就随之消失了,但是如果只有一端被关闭,那么:

  1. 如果读的那端被关闭,而有进程试图写入数据,那么会产生 SIGPIPE 的信号错误。
  2. 如果写的那端被关闭,而有进程试图读出数据,那么 read 的系统调用会读出零个字节,也就是 EOF 的含义。

利用 pipe 这个系统调用和 dup 系的函数,我们可以组合出很多有趣的玩意儿来。

上次的一个项目,大嘴牛就用到了,具体的场景是这样的。当时大嘴牛需要一个自动化的工具可以对两个程序进行比赛。这两个程序分别是一个国际象棋程序,主要的目的就是像看看让电脑左右手互博是什么情况,并同时对国际象棋的智能算法进行改进。对于这个国际象棋程序而言,他从标准输入读入信息,进行计算,随后反馈到标准输出。而大嘴牛的这个自动化工具必须 fork 出两个进程,让国际象棋程序一个扮演红方,一个扮演黑方,通过控制和过滤标准输入和输出,对两个程序进行自动化的测试,其中就用到了 pipe 的系统调用。大致的样子是这样的:

Written by dazuiniu

June 19th, 2010 at 8:54 pm

Posted in all about dev

Tagged with , ,

cout的出错

View Comments

看到 Vimer 的博客上有讲到一个有趣的 bug [1],确实是以前没有注意到的问题,很有收获。希望 Vimer 博客能再接再厉,继续写出这么优秀的文章来,特别是这种类型的文章 :-)

光看呢,还不够,大嘴牛要试着去重现这个问题,从该文的截图来看,输出到终端获得了 EPIPE 的错误信息,从而使得无法正确输出。有理由相信,这几行的片段都在一个循环里面,每次的输出的时候都会重定向标准输出的文件描述符,这个重定向可能是程序员手动做的,也可能是 CGI 框架所包含的内容。

怎么才能重现 cout 的这种一次出错之后,如果不清空 cout,那么以后的输出都会有问题的这个 bug 呢?看看大嘴牛攒出来的这个小程序,从里面输出的字符串应该可以猜出来整个流程。

#include <iostream>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int ac, char **av)
{
const char *file = "test.out";
if (ac == 2)
file = av[1];

int stdout_backup = dup(1);
int fd = open(file, O_RDWR | O_CREAT, 0644);
if (fd < 0)
{
cerr << "error opening file " << file << endl;
return -1;
}

dup2(fd, 1);
cout << "this string goes to the file." << endl;

// close the fd
close(1);
close(fd);
cout << "this would be an error." << endl;

dup2(stdout_backup, 1);
cout << "this would have gone to stdout, but due to the previous error, it fails." << endl;

cout.clear();
cout << "trying to write to stdout again. Hey! Hello Stdout Again!" << endl;

return 0;
}

cout 在被关闭文件描述符之后发生了错误,随后即使该标准输出已经被恢复,cout 仍然无法正常输出,直到大嘴牛重新清空 cout 流之后。

那么 printf 是怎么样子的呢?看看下面的程序。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int ac, char **av)
{
const char *file = "test.out";
if (ac == 2)
file = av[1];

int stdout_backup = dup(1);
int fd = open(file, O_RDWR | O_CREAT, 0644);
if (fd < 0)
{
printf("error opening file\n");
return -1;
}

if (dup2(fd, 1) < 0)
fprintf(stderr, "error dup");
printf("this string goes to the file.\n");
fflush(stdout);   /* <--- */

// close the fd
close(1);
close(fd);
printf("this would be an error.\n");
fflush(stdout);   /* <--- */

dup2(stdout_backup, 1);
printf("printf is different with cout, it succeeds here.\n");
fflush(stdout);   /* <--- */

return 0;
}

可以看到,当标准被关闭之后,随后的输出出现了问题,但是只要该文件描述符之后又好了,那么 printf 即又可以正常工作了。这里需要注意的一点是 fflush 的公用,如果没有这里的强制 flush,那么 printf 会放到 buffer 里面,当然前面可以加 setbuf 告诉他不要缓冲。

很有意思,大嘴牛可以重现这个问题,但是至于原文中为什么会出现这样的问题,Dante 说到有可能是底层库的问题,这个大嘴牛不敢妄言了,估计只有真正遇到了这么诡异的问题才可能知道了。呵呵

[1]. http://www.vimer.cn/2010/06/qq%E9%A4%90%E5%8E%85%E5%85%AC%E6%B5%8B%EF%BC%8C%E5%85%A5%E5%8F%A3cgi%E7%9A%84%E4%B8%80%E4%B8%AAbug%E5%AE%9A%E4%BD%8D.html

Written by dazuiniu

June 14th, 2010 at 6:39 pm

Posted in all about dev

Tagged with , , ,