1

以下のヘルパー関数を使用して、コマンドを実行し、posix システムで戻り値を取得します。以前は を使用していましたが、アプリケーションを実行して終了する前にpopenアプリケーションのリターン コードを取得することは不可能でした。popenpopenpclose

次のヘルパー関数は、プロセス フォークを作成しexecvp、目的の外部プロセスを実行するために使用し、次に、親waitpidがリターン コードを取得するために使用します。実行を拒否する奇妙なケースが見られます。

wait=trueで呼び出された場合waitpid、何があってもアプリケーションの終了コードを返す必要があります。ただし、stdout戻りコードがゼロ以外である必要があることを指定する出力が表示されますが、戻りコードはゼロです。通常のシェルで外部プロセスをテストすると、echoing$?はゼロ以外を返すため、外部プロセスが正しいコードを返さないという問題はありません。それが助けになる場合、実行されている外部プロセスは次のとおりですmount(8)(はい、使用できることはわかっていますmount(2)が、それは重要ではありません)。

コードダンプについては事前にお詫び申し上げます。そのほとんどはデバッグ/ロギングです:

inline int ForkAndRun(const std::string &command, const std::vector<std::string> &args, bool wait = false, std::string *output = NULL)
{
    std::string debug;

    std::vector<char*> argv;
    for(size_t i = 0; i < args.size(); ++i)
    {
        argv.push_back(const_cast<char*>(args[i].c_str()));
        debug += "\"";
        debug += args[i];
        debug += "\" ";
    }
    argv.push_back((char*)NULL);

    neosmart::logger.Debug("Executing %s", debug.c_str());

    int pipefd[2];

    if (pipe(pipefd) != 0)
    {
        neosmart::logger.Error("Failed to create pipe descriptor when trying to launch %s", debug.c_str());
        return EXIT_FAILURE;
    }

    pid_t pid = fork();

    if (pid == 0)
    {
        close(pipefd[STDIN_FILENO]); //child isn't going to be reading
        dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
        close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd
        dup2(pipefd[STDOUT_FILENO], STDERR_FILENO);

        if (execvp(command.c_str(), &argv[0]) != 0)
        {
            exit(EXIT_FAILURE);
        }
        return 0;
    }
    else if (pid < 0)
    {
        neosmart::logger.Error("Failed to fork when trying to launch %s", debug.c_str());
        return EXIT_FAILURE;
    }
    else
    {
        close(pipefd[STDOUT_FILENO]);

        int exitCode = 0;

        if (wait)
        {
            waitpid(pid, &exitCode, wait ? __WALL : (WNOHANG | WUNTRACED));

            std::string result;
            char buffer[128];
            ssize_t bytesRead;
            while ((bytesRead = read(pipefd[STDIN_FILENO], buffer, sizeof(buffer)-1)) != 0)
            {
                buffer[bytesRead] = '\0';
                result += buffer;
            }

            if (wait)
            {
                if ((WIFEXITED(exitCode)) == 0)
                {
                    neosmart::logger.Error("Failed to run command %s", debug.c_str());
                    neosmart::logger.Info("Output:\n%s", result.c_str());
                }
                else
                {
                    neosmart::logger.Debug("Output:\n%s", result.c_str());
                    exitCode = WEXITSTATUS(exitCode);
                    if (exitCode != 0)
                    {
                        neosmart::logger.Info("Return code %d", (exitCode));
                    }
                }
            }

            if (output)
            {
                result.swap(*output);
            }
        }

        close(pipefd[STDIN_FILENO]);

        return exitCode;
    }
}

コマンドは正しいパラメーターで正常に実行され、関数は問題なく続行され、WIFEXITEDが返されることに注意してくださいTRUE。ただし、WEXITSTATUS何か他のものを返す必要がある場合は、0 を返します。

4

3 に答える 3

2

おそらくあなたの主な問題ではありませんが、小さな問題があると思います。あなたの子プロセスでは、あなたは...

dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO); //but wait, this pipe is closed!

しかし、あなたが望むのは次のとおりだと思います:

dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO);
close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd for both, can close

Linux でのフォークとパイプの経験はあまりありませんが、最近似たような関数を書きました。必要に応じて、比較するコードを確認できます。私は自分の機能が機能することを知っています。

execAndRedirect.cpp

于 2012-11-01T17:30:47.857 に答える
2

私はマングースライブラリを使用しており、コードをgrepすると、マングースからSIGCHLD使用すると.mg_startSIGCHLDSIG_IGN

waitpidman pageから、Linux ではゾンビプロセスを作成しないためSIGCHLD、プロセスが正常に実行されて終了した場合は失敗しますが、まだ実行されていない場合は正常に実行されます。これが私のコードの散発的な失敗の原因でした。SIG_IGNwaitpid

まったく何もしない void 関数をSIGCHLD呼び出した後に再設定するだけで、ゾンビの記録がすぐに消去されないようにすることができました。mg_start

@Geoff_Monteeのアドバイスによると、 のリダイレクトにバグがありましたが、戻り値をまたはに格納するのではなく、親プロセス (ゾンビ レコード) に関連付けられたカーネル オブジェクトに格納STDERRするため、これは問題の原因ではありませんでした。execvpSTDERRSTDOUT

C++の非連続性に関する@jilles の警告vectorは、C++03 以降には適用されません (C++98 でのみ有効ですが、実際には、ほとんどの C++98 コンパイラは連続したストレージを使用していました)。この問題に関連しています。ただし、ブロックして出力をチェックする前にパイプから読み取ることに関するアドバイスはwaitpid的を射ています。

于 2012-11-01T18:37:26.063 に答える
0

pcloseドキュメントに反して、プロセスが終了するのをブロックして待機しないことがわかりました(これはCentOS 6にあります)。pclose呼び出してから呼び出しwaitpid(pid,&status,0);て、真の戻り値を取得する必要があることがわかりました。

于 2015-03-26T21:14:59.377 に答える