1

我有下面的辅助函数,用于在 posix 系统上执行命令并获取返回值。我曾经使用,但如果应用程序在/有机会完成其工作之前运行并退出,popen则无法获取应用程序的返回码。popenpopenpclose

下面的辅助函数创建一个进程分支,用于execvp运行所需的外部进程,然后父进程waitpid用于获取返回码。我看到它拒绝运行的奇怪情况。

wait当使用=调用时truewaitpid无论如何都应该返回应用程序的退出代码。但是,我看到stdout指定返回码不为零的输出,但返回码为零。在常规 shell 中测试外部进程,然后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

我正在使用mongoose库,并且 grepping 我的代码SIGCHLD显示使用mg_startfrom mongoose 会导致设置SIGCHLDSIG_IGN.

waitpid手册页来看,在 Linux上,SIGCHLD设置为SIG_IGN不会创建僵尸进程,因此waitpid如果进程已经成功运行并退出,则会失败 - 但如果尚未成功,则会运行正常。这是我的代码偶尔失败的原因。

SIGCHLD在调用一个完全不执行任何操作的 void 函数后简单地重新设置mg_start就足以防止僵尸记录被立即删除。

根据@Geoff_Montee 的建议,我的重定向中有一个错误STDERR,但这不是问题的原因,因为execvp它没有将返回值存储在STDERR甚至STDOUT,而是存储在与父进程(僵尸记录)关联的内核对象中。

@jilles关于 C++ 中不连续性的警告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 回答