C++ popen pipe не закрывается корректно с помощью pclose для команды "ps aux"

Я использую MacOS и хочу выполнить команду "ps aux" и получить ее вывод через мое приложение. Я написал метод, который выполняет команды, используя функцию popen:

std::string exec(const char* cmd) {

    char buffer[128];
    std::string result = "";

    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!2");
    try {
        while (!feof(pipe)) {
            if (fgets(buffer, 128, pipe) != NULL)
                result += buffer;
        }
    } catch (...) {
        pclose(pipe);

        throw;
    }
    pclose(pipe);


    return result;
}

У меня есть цикл, который постоянно выполняет функцию exec("ps aux"). Проблема в том, что канал из popen не закрыт, что я проверил, используя команду "lsof" из терминала. Примерно через 20 секунд приложение открывает около 300 дескрипторов файлов, которые не позволяют приложению открывать больше каналов (выполняя команду "ps aux") из цикла.

Я обнаружил, что функция exec отлично работает для других команд (канал закрывается правильно), например, "netstat", поэтому в команде "ps aux" должно быть что-то, что препятствует закрытию канала.

Я много искал об этой проблеме, но не нашел никакого решения. Может кто-нибудь, пожалуйста, укажите мне в правильном направлении?

Спасибо!

1 ответ

Я не вижу, что конкретно не так с вашим кодом. Для этих вещей я использую пользовательское средство удаления с std::unique_ptr чтобы убедиться, что файл закрывается на всех возможных выходах.

Также обратите внимание, что не рекомендуется использовать цикл while(eof(...)) по нескольким причинам. Один из них не установлен в случае ошибки. Больше информации здесь.

// RAII piped FILE*

// custom deleter for unique_ptr
struct piped_file_closer
{
    void operator()(std::FILE* fp) const { pclose(fp); }
};

// custom unique_ptr for piped FILE*
using unique_PIPE_handle = std::unique_ptr<std::FILE, piped_file_closer>;

//
unique_PIPE_handle open_piped_command(std::string const& cmd, char const* mode)
{
    auto p = popen(cmd.c_str(), mode);

    if(!p)
        throw std::runtime_error(std::strerror(errno));

    return unique_PIPE_handle{p};
}

// exception safe piped reading
std::string piped_read(std::string const& cmd)
{
    std::string output;

    if(auto pipe = open_piped_command(cmd, "r"))
    {
        char buf[512];
        while(auto len = std::fread(buf, sizeof(char), sizeof(buf), pipe.get()))
            output.append(buf, len);

        if(std::ferror(pipe.get()))
            throw std::runtime_error("error reading from pipe");
    }

    return output;
}

призвание auto output = piped_read("ps aux"); сотни раз в моей системе не выдает ошибку с этим кодом.

Другие вопросы по тегам