【问题标题】:Emulating process substitution using named pipes使用命名管道模拟进程替换
【发布时间】:2018-02-28 23:04:37
【问题描述】:

我正在尝试在 .NET 中启动子进程并将其输出重定向到命名管道。在子进程中写入标准输出的尝试应该被阻塞,直到客户端实际从命名管道中读取。

这是我的代码:

using System;
using System.Diagnostics;
using System.IO.Pipes;

namespace psub
{
    class Program
    {
        static void Main(string[] args)
        {
            var pipeName = "testpipe"; // Guid.NewGuid().ToString();
            Console.WriteLine($@"\\.\pipe\{pipeName}");

            var psi = new ProcessStartInfo()
            {
                FileName = "ping",
                Arguments = "google.com",

                UseShellExecute = false,
                RedirectStandardOutput = true
            };

            using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.Out))
            using (var process = new Process { StartInfo = psi })
            {
                pipeServer.WaitForConnection();
                process.Start();
                process.StandardOutput.BaseStream.CopyTo(pipeServer);
                process.WaitForExit();
            }
        }
    }
}

当我启动这个可执行文件时,它在pipeServer.WaitForConnection(); 处阻塞,正如预期的那样。然后我打开cmd.exe 并运行type \\.\pipe\testpipe,尝试从命名管道[1] 中读取。

这会导致以下错误(在 cmd 会话中):

All pipe instances are busy.

此外,在 C# 程序中,pipeServer.WaitForConnection 完成,然后执行继续到 ...CopyTo(pipeServer),它会爆炸:

Unhandled Exception: System.IO.IOException: Pipe is broken.

我不明白为什么会发生 All pipe instances are busy. 错误。

据我了解,有一个服务器(在 C# 程序中实例化)将写入命名管道。一旦执行到达CopyTo,数据就会被传送到程序的单个阻塞线程上的命名管道服务器。

还有一个命名管道客户端(type \\.\pipe\testpipe 进程),它将释放服务器尝试写入的数据的命名管道。

那么为什么会出现“所有管道实例都忙”的错误呢?

我尝试将new NamedPipeServerStream(pipeName, PipeDirection.Out) 调整为new NamedPipeServerStream(pipeName, PipeDirection.Out, 2) 以及其他一些调整,但没有成功,但对问题所在或如何解决问题没有任何真正的了解。有人可以像我五岁一样解释一下吗?


[1]:目标是模拟 UNIX shell 中可用的 process substitution 功能,因此我必须能够从 \\.\pipe\<some file name> 中读取,而不是使用 C++ 或 .NET 为管道。

【问题讨论】:

  • Pipe is broken on write 说管道句柄已经关闭
  • @RbMm 是的,我明白这一点,但它为什么死了?问题是原始的All pipe instances are busy. 错误消息,导致管道死亡,但为什么从管道读取完全是错误?

标签: c# winapi named-pipes


【解决方案1】:

当您在 cmd 中使用 type <some_file_name> 时 - 它首先为此 <some_file_name> 调用 GetFileAttributes

GetFileAttributes 内部(内核内部)临时打开文件,用于查询它的属性(确实 - 为此需要查询文件系统设备并发送查询请求 - 需要打开文件)。

对于命名管道文件,这具有致命的副作用 - 服务器端已连接(在打开的文件上)并且刚刚断开连接(在关闭时)。

仅在此 cmd 之后(再次)打开文件

但您的管道服务器已经没有监听(断开连接后)。结果 cmd 通常收到 STATUS_PIPE_NOT_AVAILABLE 错误 - 在侦听状态下找不到命名管道的实例。 - 它转换为 win32 错误 ERROR_PIPE_BUSY - 所有管道实例都忙

作为单独的注释 - 3 个不同的状态错误:

STATUS_INSTANCE_NOT_AVAILABLE已达到最大命名管道实例数。

STATUS_PIPE_NOT_AVAILABLE在监听状态下找不到命名管道的实例。

STATUS_PIPE_BUSY 转换为同一个win32 错误ERROR_PIPE_BUSY。但在你的情况下,原来的错误正是STATUS_PIPE_NOT_AVAILABLE

结论 - cmd type 命令并非旨在与管道交互。或者作为替代方案,您需要创建最少 2 个管道如何以相同的管道名称结束。

【讨论】:

  • 感谢您对此进行调查。 GetFileAttributes 调用是否也是我得到的ERROR_PIPE_BUSY 的潜在因素(可能CreateFileGetFileAttributes 同时发生)?我将尝试设置服务器,以便将文件提供给在一个时间跨度内连接的无限数量的客户端。
  • PS:你能告诉我你用什么工具来跟踪底层的 Windows API 调用吗?
  • 错误STATUS_PIPE_NOT_AVAILABLE 得到cmd.exe on call CreateFile。它把它翻译成ERROR_PIPE_BUSY。我使用调试器。 cmd.exe 中的调用栈
  • 有 2 个正在运行的管道在测试管道上结束(在写入后关闭并在断开连接事件上再次开始侦听)cmd 正确打印消息与我写入,但是这是随机的。 cmd 不设计为通过类型使用命名管道。
  • @AsadSaeeduddin - 但是,如果您使用重定向的 std 句柄而不是 type 命令 - 一切正常
【解决方案2】:

您无法通过 Windows 命名管道的命令行界面进行访问。请参阅http://en.wikipedia.org/wiki/Named_pipe 了解更多信息

【讨论】:

  • 这是错误的。我可以使用tools which directly work with the Windows API 轻松创建命名管道,并使用echo foo > \\.\pipe\footype \\.\pipe\foo 与它们交互。一个演示:i.imgur.com/vq6gwBd.gifv
  • 在 Windows 上,命名管道使用 Win32 CreateFile() 函数连接,这是按名称打开文件的最常用方法。按理说,命令行窗口的type 命令会使用CreateFile() 打开一个请求的文件,在这种情况下它也可以打开一个命名管道。 busy 错误消息也很明显,这是 CreateFile() 确实报告的错误:“如果至少有一个活动管道实例但服务器上没有可用的侦听器管道,这意味着所有管道实例当前已连接,CreateFile 失败并显示 ERROR_PIPE_BUSY。"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多