首页 > 解决方案 > 忽略子进程的标准输出,危险吗?

问题描述

我有一个应用程序,它启动 1-45 个进程(依赖于工作)并通过命名管道与它们通信。我只stderr对子进程感兴趣,对重定向进程没有兴趣stdout

我编写的代码正是这样做的,我重定向stderr而不是重定向stdout,一切都按预期工作。当我说我的意思是,我得到了重定向stderr,我没有看到任何stdout出现在父进程控制台上,也没有因为没有重定向而出现任何死锁stdout

我关心的是,stdout子进程的去向在哪里?这些进程倾向于将大量文本转储到stdout,我担心它们会填充一些内部buffer内容,这将导致子进程block在写入stdout.

在过去,我还重定向stdout并只是转储(什么都不做)从异步事件提供的数据。但这样做会产生严重的后果。如上所述,我通过named pipes与子进程进行通信。我注意到的是,当我重定向stdout它时,它会影响父进程write操作的性能named pipes

当不重定向stdout写入时间通常< 1ms但重定向后stdout时间跳转到> 4s...

我的问题是,只重定向是否安全stderr

下面是我用来包装Process. 我还使用该完善的extension方法await来完成该过程。我没有发布,但如果您不熟悉它,我可以发布。

public class ChildProcess
{
    public async Task StartProcessAsync( string executablePath, CancellationToken token = default )
    {
        if ( !File.Exists( executablePath ) )
            throw new FileNotFoundException( "The specified rip executable path does not exist", executablePath );

        using var process = new Process( );          
        process.StartInfo.FileName = executablePath;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.ErrorDialog = false;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.RedirectStandardError = true;

        await StartProcess( process, StdErrorOutput, token ).ConfigureAwait( false );

        static void StdErrorOutput( object sender, DataReceivedEventArgs args )
        {
            if ( string.IsNullOrWhiteSpace( args.Data ) ) return;
        }
    }

    private Task StartProcess( Process process,
        DataReceivedEventHandler? stderr, CancellationToken token = default )
    {
        return Task.Run( async ( ) =>
        {
            try
            {
                if ( stderr is object )
                    process.ErrorDataReceived += stderr;

                if ( !process.Start( ) )
                    throw new ApplicationException( $"Failed to start '{process.StartInfo.FileName}'" );

                if ( stderr is object )
                    process.BeginErrorReadLine( );

                await process.WaitForExitAsync( token ).ConfigureAwait( false );
            }
            finally
            {
                SafeKill( );

                if ( stderr is object )
                    process.ErrorDataReceived -= stderr;
            }

            void SafeKill( )
            {
                try
                {
                    if ( !process.HasExited )
                    {
                        process.Refresh( );

                        if ( stderr is object )
                            process.CancelErrorRead( );

                        process.Kill( );
                    }
                }
                catch ( Exception ex )
                {
                    Console.WriteLine( ex );
                }
            }

        } );
    }
}

编辑

在阅读了 Eryk Sun 留下的评论后,我决定深入研究Process源代码,看看我是否能弄清楚发生了什么。主要是我有兴趣查看STARTUPINFO仅指定重定向之一时如何填充结构std streams

我删除了一堆我不感兴趣的代码。据我所知,如果我设置RedirectStandardErrortrue指定结构成员设置RedirectStandardOutput为. 这导致 上的属性为。falseSTARTIPINFOhStdOutputstdoutStandardOutputProcess classnull

如果RedirectStandardOutput设置为true子进程的输出,则通过pipe. 然后创建一个StreamReader实例并将其分配给StandardOutput。然后StreamReader从创建的读取pipe以获取子进程的输出。

因此,如果HANDLE设置为stdoutwhen RedirectStandardOutput设置为false,为什么我在父进程控制台窗口上看不到输出?输出去哪儿了?

System.Diagnostics.Process.StartWithCreateProcess

资源

SafeFileHandle? childInputPipeHandle = null;
SafeFileHandle? parentOutputPipeHandle = null;

if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
{
    if (startInfo.RedirectStandardOutput)
    {
        CreatePipe(out parentOutputPipeHandle, out childOutputPipeHandle, false);
    }
    else
    {
         childOutputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
    }

    startupInfo.hStdOutput = childOutputPipeHandle.DangerousGetHandle();
    startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
}

/* Code starting the process removed */

if (startInfo.RedirectStandardOutput)
{
    Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
    _standardOutput = new StreamReader(new FileStream(parentOutputPipeHandle!, FileAccess.Read, 4096, false), enc, true, 4096);
 }

标签: c#windowsprocess

解决方案


推荐阅读