2011-03-21 101 views
0

我将一些Unix代码移植到Windows,它将stderr和stdout重定向到我创建的管道,并且有一个线程从此管道读取,然后将输出发送到调试控制台。这在Unix上运行良好,但我无法在Windows上运行。当管道的读取面关闭时出现问题。它不会将EOF写入会导致线程退出的管道,而是死锁。为什么?关闭管道时发生死锁fd

一种解决方法是跳过电话关闭,这让我很担心,但由于我的过程很短暂,也许这不是什么大不了的事?

下面是示例代码说明了这个问题......我使用VS 2010:

#include <cstdio> 
#include <tchar.h> 
#include <iostream> 
#include <vector> 
#include <fcntl.h> 
#include <Windows.h> 
#include <io.h> 
#define posix_open _open 
#define posix_read _read 
#define posix_write _write 
#define posix_pipe(fds) _pipe(fds, 8096, _O_BINARY) 
#define posix_close _close 
#define posix_dup _dup 
#define posix_dup2 _dup2 
#define posix_fileno _fileno 

using namespace std; 

static const int PIPE_READ = 0; 
static const int PIPE_WRITE = 1; 

DWORD __stdcall PipeReaderFunc(void* readFd) 
{ 
    int pipeFd = *((int*)readFd); 
    vector<char> buffer(8096); 
    while(posix_read(pipeFd, &buffer[0], buffer.size()) != 0) 
    { 
     OutputDebugString(&buffer[0]); 
    } 
    return 0; 
} 

void test() 
{ 
    int pipefd[2] = {-1,-1}; 
    if(posix_pipe(pipefd) < 0) 
    { throw std::exception("Failed to initialize pipe.");} 

    int stdoutOrig = posix_dup(_fileno(stdout)); 
    int stderrOrig = posix_dup(_fileno(stderr)); 
    if(-1 == posix_dup2(pipefd[PIPE_WRITE], posix_fileno(stdout))) // closes stdout 
    {throw exception("Failed to dup stdout fd.");} 

    if(-1 == posix_dup2(pipefd[PIPE_WRITE], posix_fileno(stderr))) // closes stderr 
    {throw exception("Failed to dup stderr fd.");} 

    HANDLE hThread = CreateThread(NULL, 0, PipeReaderFunc, &pipefd[PIPE_READ], 0, NULL); 
    if(NULL == hThread) 
    {throw exception("Failed to create thread");} 

    cout << "This should go to the debug console" << endl; 
    Sleep(1000); // Give time for the thread to read from the pipe 

    posix_close(stdoutOrig); 
    posix_close(stderrOrig); 
    posix_close(pipefd[PIPE_WRITE]); 

    // Deadlock occurs on this line 
    posix_close(pipefd[PIPE_READ]); 

    // This is commented out because it has no effect right now. 
    //WaitForSingleObject(hThread, INFINITE); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    try 
    { test(); } 
    catch(exception& ex) 
    { cerr << ex.what() << endl; } 
    return 0; 
} 

感谢如何解决这个任何想法!

回答

3

当在阻止时调用_close时,窗口上的_read实现可能会返回-1。如果在您的_close完成后调用它,则应按文档返回-1。所以,看起来你的线程会卡在主循环中,因为它只会在返回值为零时终止。也许你应该改变你的循环条件从!= 0> 0并尝试它。


编辑:

我一直在寻找在文档的错误部分。根据文档,当所有指向它的描述符都关闭时,管道句柄关闭。我想你想实现这个,但是犯了一个小错误。如果更换:

posix_close(stdoutOrig); 
posix_close(stderrOrig); 

posix_close(posix_fileno(stdout)); 
posix_close(posix_fileno(stderr)); 

的程序执行,并正确终止。这是因为有两个指向阅读器句柄的fds,而你只关闭了一个。所以线程被高兴地阻塞,等待更多的数据。当您还关闭dup ed句柄时,read返回零(如您所指出的),并且线程终止。

+0

啊是的,这是正确的。在Unix上,当管道的读取结束时关闭EOF。在Windows上,你不会得到EOF,直到引用管道的所有fds都关闭,并且我没有关闭它们。现在完美。谢谢! – Skrymsli 2011-03-22 16:32:10

0

如果您正在使用fork()exec*()家庭在Windows在Unix而不是CreateThread()功能之一,子进程将关闭读取和执行dup2()操作之后,做Exec之前写管道的末端。父进程将关闭它不会使用的管道的任何一端。这是必要的(一般来说)以确保没有引用管道的杂散开放文件描述符。如果管道上有杂散开放的写入端,管道上的读卡器将永远不会收到EOF。如果管道上有一个杂散的开放读取端,那么作者可能会阻止(死锁)等待读者读取数据 - 即使读者与写入过程相同。

通过类比,你应该确保你的线程只有它需要打开的管道的末端,并且应该关闭所有其他部分。