Каков надежный способ получить выходные данные от экземпляров процессов в C ++? - PullRequest
0 голосов
/ 05 сентября 2018

По сути, я хочу создать экземпляр программы в оболочке Linux и получить выходные данные, которые она генерирует, для вывода в строку или список строк (в зависимости от строки). Программа, которую я хочу запустить в своем приложении c ++, это просто ps aux.

  • Все найденные мной «решения» не полностью решили мою проблему. Большинство из них попали под ошибку, описанную в этом ответе . В основном popen завершается неудачно и не возвращает полный вывод из выполнения оболочки. Давайте назовем это «ошибкой 1» ради этого вопроса.

Что я пробовал до сих пор:

1) Используя boost, я попробовал это, следуя их документации :

#include <boost/process.hpp>
namespace bp = boost::process;
bool is_process_running(std::string p_name){
    string cmd = "ps aux";
    bp::ipstream out;
    std::string line;
    bp::child c(cmd, bp::std_out > out);

    // the while is supposed to read each line of the output
    // but the execution doesn't even enter the while
    while(c.running() && std::getline(out, line) && !line.empty())
    {
        if(line.find(p_name) != std::string::npos)
        {
            return true;
        }
    }
    c.wait();
    return false;
}

2) Я также попробовал:

bool is_process_running(std::string p_name){
    string cmd = "ps aux";
    std::array<char, 256> buffer;
    std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 256, pipe.get()) != nullptr)
        {
            // here the line comes incomplete
            std::string line(buffer.data());
            if(line.find(p_name) != std::string::npos)
            {
                return true;
            }

        }
    }
    return false;
}

Но этот последний тоже попал в «ошибку 1».

3) Этот фрагмент кода попал в «ошибку 1»
4) Этот также попал в «ошибку 1»
Я не знаю, это необходимо, чтобы поставить коды для 3) и 4), потому что это буквально то, что в этих ответах, я ничего не изменил, но если вам, ребята, нужно, я могу отредактировать свой вопрос. Поэтому мой вопрос заключается в следующем: как получить вывод команды таким образом, чтобы он работал? Заранее спасибо.

Редактировать : Я пробовал с предоставленным кодом:

bool is_process_running(std::string p_name)
    FILE* p = popen(cmd.c_str(), "r");
    if (!p) { cerr << "oops"; return 2; }
    char line[500];
    while (fgets(line, 500, p))
    {
        std::string linha(line);
        if(linha.find(p_name) != std::string::npos)
            return true;
    }
    fclose(p);
    return false;
}

В этом случае, вот пример усеченного вывода из popen / fgets:

[fabio ~]$ ps aux | grep 391
root       391  0.0  0.1  48580 12864 ?        Ss   Sep03   0:06 /usr/lib/systemd/systemd-journald
fabio 15435  0.0  0.0 112548   960 pts/2    S+   15:40   0:00 grep --color=auto 391

Строка для процесса 391 такая же, как и эта, но во время выполнения она возвращает только мне "root 391 0.0 0.1 48580 12856 ? Ss Sep03 0:06 /usr/lib/system\n"

1 Ответ

0 голосов
/ 05 сентября 2018

Используйте соответствующие системные вызовы напрямую : pipe (2) , fork (2) , dup2 (2) , закрыть (2) , execve (2) , waitpid (2) . Прочитайте хорошую книгу по программированию для Linux (возможно, старую ALP , в которой есть несколько глав, связанных с вашим вопросом). Ищите вдохновение в исходном коде других бесплатных программ, работающих с конвейерами (например, какой-нибудь простой оболочки, такой как sash) и / или в вашем libc (или musl-libc , он очень читабелен код) для кода popen.

У нас нет места и времени для подробного объяснения, но вы найдете много ресурсов в Интернете, и ALP можно загрузить бесплатно; конечно execve - это последний системный вызов, который обычно выполняется в дочернем процессе, поскольку он повторно инициализирует виртуальное адресное пространство.

Для чтения с канала без ограничения длины строки. Либо используйте read (2) вручную (и позаботьтесь о буферизации), либо рассмотрите getline (3) (см. Также f dopen (3) )

Вас могут заинтересовать такие фреймворки, как POCO или Qt .

Вы, вероятно, хотите избежать popen (3) (и вам не нужно что-то, используя /bin/sh, если только ваша команда не нуждается в расширении для globbing ; см. Глоб (7) ). * +1048 *

Помните о сигнале (7) и о безопасности сигнала (7) (и, возможно, опрос (2) ...)


Программа, которую я хочу запустить в своем приложении c ++, это просто ps aux

Тогда вам, вероятно, следует не запускать какой-либо внешний процесс, а вместо этого обращаться /proc/ напрямую (см. proc (5) ; вам понадобится opendir (3) , readdir (3) и т. д ...) или, возможно, через libproc. Конечно, ps aux обращается к самому /proc/ (поэтому вы не получите ничего, кроме некоторой простоты, запустив ps, и потеряете некоторую производительность и некоторую надежность при его использовании). См. Также это , это , это , это , это ответы.

В комментарии вы упоминаете

Мне нужно знать, запущены ли некоторые процессы, учитывая как его имя, так и аргументы.

Это хороший случай для программного доступа к /proc/: цикл в каталоге /proc/ с opendir, цикл readdir, enddir. Для каждой записи, заданной readdir, убедитесь, что она имеет числовое имя (начиная с цифры), в противном случае пропустите ее. С этим числовым именем (например, 1234) создайте путь, подобный /proc/1234/cmdline и , откройте его, затем проанализируйте его содержимое (у него есть разделяющие NUL байты). Это, вероятно, не сложнее, чем запуск процесса ps aux, получение и анализ его выходных данных, и, безусловно, более эффективный. (Чтобы разобраться в деталях, запустите od -cx /proc/self/cmdline, затем od -cx /proc/$(pidof systemd-journald)/cmdline и поймите его вывод).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...