убить процесс, начатый с popen - PullRequest
20 голосов
/ 14 февраля 2009

После открытия канала для процесса с popen, есть ли способ убить запущенный процесс? (Использование pclose не то, что я хочу, потому что это будет ждать завершения процесса, но мне нужно его убить.)

Ответы [ 8 ]

26 голосов
/ 14 февраля 2009

Не используйте popen (), а пишите свою собственную обертку, которая делает то, что вы хотите.

Довольно просто выполнить fork (), а затем заменить stdin & stdout. используя dup2 (), а затем вызывая exec () для вашего ребенка.

Таким образом, у вашего родителя будет точный идентификатор ребенка, и вы можете использовать kill () на этом.

Поиск Google для "реализации popen2 ()" для некоторого примера кода на как реализовать то, что делает popen (). Это всего лишь дюжина строк долго. Взято с dzone.com мы можем видеть пример, который выглядит так:

#define READ 0
#define WRITE 1

pid_t
popen2(const char *command, int *infp, int *outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
        return -1;

    pid = fork();

    if (pid < 0)
        return pid;
    else if (pid == 0)
    {
        close(p_stdin[WRITE]);
        dup2(p_stdin[READ], READ);
        close(p_stdout[READ]);
        dup2(p_stdout[WRITE], WRITE);

        execl("/bin/sh", "sh", "-c", command, NULL);
        perror("execl");
        exit(1);
    }

    if (infp == NULL)
        close(p_stdin[WRITE]);
    else
        *infp = p_stdin[WRITE];

    if (outfp == NULL)
        close(p_stdout[READ]);
    else
        *outfp = p_stdout[READ];

    return pid;
}

NB. Похоже, что popen2 () - это то, что вам нужно, но мой дистрибутив, похоже, не идет с этим методом.

5 голосов
/ 08 октября 2012

Вот улучшенная версия popen2 (кредит принадлежит Сергею Л.). Версия, опубликованная slacy, не возвращает PID процесса, созданного в popen2, но PID, присвоенный sh.

pid_t popen2(const char **command, int *infp, int *outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
        return -1;

    pid = fork();

    if (pid < 0)
        return pid;
    else if (pid == 0)
    {
        close(p_stdin[WRITE]);
        dup2(p_stdin[READ], READ);
        close(p_stdout[READ]);
        dup2(p_stdout[WRITE], WRITE);

        execvp(*command, command);
        perror("execvp");
        exit(1);
    }

    if (infp == NULL)
        close(p_stdin[WRITE]);
    else
        *infp = p_stdin[WRITE];

    if (outfp == NULL)
        close(p_stdout[READ]);
    else
        *outfp = p_stdout[READ];

    return pid;
}

Новая версия должна вызываться с

char *command[] = {"program", "arg1", "arg2", ..., NULL};
4 голосов
/ 14 февраля 2009

popen на самом деле не запускает поток, а скорее разветвляет процесс. Когда я смотрю на определение, не похоже, что есть простой способ получить PID этого процесса и убить его. Могут быть сложные способы, такие как исследование дерева процессов, но я думаю, вам лучше использовать функции pipe, fork и exec для имитации поведения popen. Затем вы можете использовать PID, полученный из fork (), чтобы убить дочерний процесс.

3 голосов
/ 14 февраля 2009

На самом деле, если процесс выполняет ввод / вывод (что и должно быть, в противном случае, почему popen вместо system (3)?), Тогда pclose должен ударить его с SIGPIPE в следующий раз пытается читать или писать, и это должно красиво упасть: -)

0 голосов
/ 07 января 2015

Я просто вставляю скрипт (bash) в первую строку:

echo PID $$

тогда я читаю pid и использую его для: убить (PID, 9)

0 голосов
/ 20 декабря 2013

Я следую идее @slacy, которая создает функцию popen2.
Но обнаруженная проблема, когда дочерний процесс умирает, родительский процесс, который все еще читает дескриптор файла outfp, не возвращается из функции.

Это можно исправить, добавив закрыть неиспользуемый канал в родительском процессе.

close(p_stdin[READ]);
close(p_stdout[WRITE]);

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

execl("/bin/bash", "bash", "-c", command, NULL);

Когда умирает дочерний процесс, вызывающая функция popen2 должна получить статус дочернего процесса путем вызова pclose2. если мы не собираем этот статус, дочерний процесс может быть зомби-процессом, когда он завершается.

Это полный код, который протестирован и работает, как и ожидалось.

#define READ 0
#define WRITE 1

pid_t
popen2(const char *command, int *infp, int *outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
        return -1;

    pid = fork();

    if (pid < 0)
        return pid;
    else if (pid == 0)
    {
        dup2(p_stdin[READ], STDIN_FILENO);
        dup2(p_stdout[WRITE], STDOUT_FILENO);

     //close unuse descriptors on child process.
     close(p_stdin[READ]);
     close(p_stdin[WRITE]);
     close(p_stdout[READ]);
     close(p_stdout[WRITE]);

        //can change to any exec* function family.
        execl("/bin/bash", "bash", "-c", command, NULL);
        perror("execl");
        exit(1);
    }

    // close unused descriptors on parent process.
    close(p_stdin[READ]);
    close(p_stdout[WRITE]);

    if (infp == NULL)
        close(p_stdin[WRITE]);
    else
        *infp = p_stdin[WRITE];

    if (outfp == NULL)
        close(p_stdout[READ]);
    else
        *outfp = p_stdout[READ];

    return pid;
}

int
pclose2(pid_t pid) {
    int internal_stat;
    waitpid(pid, &internal_stat, 0);
    return WEXITSTATUS(internal_stat);
}
0 голосов
/ 14 февраля 2009

Я сделал нечто подобное в python, где вы могли убить подпроцесс, и любые подпроцессы разветвились для этого. Это похоже на решение рабства на самом деле. http://www.pixelbeat.org/libs/subProcess.py

0 голосов
/ 14 февраля 2009

Очевидный путь - системный ("имя_процесса_процесса");

Очевидно, что это проблематично, если у вас запущено несколько экземпляров процесса.

...