【问题标题】:Deadlock when closing a pipe fd关闭管道 fd 时出现死锁
【发布时间】:2011-03-21 23:36:02
【问题描述】:

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

一种解决方法是跳过调用关闭,这让我有点担心,但由于我的过程是短暂的,也许这没什么大不了的?

这是说明问题的示例代码...我正在使用 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;
}

感谢您提供有关如何解决此问题的任何想法!

【问题讨论】:

    标签: c++ windows posix pipe


    【解决方案1】:

    _close 被阻塞时,Windows 上的_read 实现可能会返回-1。如果在您的 _close 完成后调用它,它应该根据文档返回 -1。因此,看起来您的线程会卡在主循环中,因为它仅在返回值为零时终止。也许您应该将循环条件从 != 0 更改为 &gt; 0 并尝试一下。


    编辑:

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

    posix_close( stdoutOrig ); 
    posix_close( stderrOrig ); 
    

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

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

    【讨论】:

    • 啊,是的,这是正确的。在 Unix 上,当管道的读取端关闭时,您会得到 EOF。在 Windows 上,直到所有引用管道的 fd 都关闭并且我没有全部关闭它们,你才会得到 EOF。现在完全有道理。谢谢!
    【解决方案2】:

    如果您在 Unix 上使用 fork()exec*() 系列函数之一,而不是在 Windows 上使用 CreateThread(),则子进程将在执行 dup2() 操作后关闭管道的读取和写入端在执行执行之前。父进程将关闭它不打算使用的管道的任何一端。这是必要的(通常),以确保没有引用管道的杂散打开文件描述符。如果管道有一个杂散的开放写入端,则管道上的读取器将永远不会获得 EOF。如果管道有一个杂散的开放读取端,写入器可能会阻塞(死锁)等待读取器读取数据 - 即使该读取器与正在执行写入的进程相同。

    以此类推,您应该确保您的线程只有需要打开的管道末端,并且应该关闭所有其他部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      • 2018-09-10
      • 1970-01-01
      • 1970-01-01
      • 2010-11-08
      • 1970-01-01
      相关资源
      最近更新 更多