23

在打开一个进程的管道后popen,有没有办法杀死已经启动的进程?(使用pclose不是我想要的,因为那会等待进程完成,但我需要杀死它。)

4

8 回答 8

27

不要使用popen(),编写自己的包装器来做你想做的事。

很简单fork(),然后用 & 替换 & stdin,然后调用你的孩子。stdoutdup2()exec()

这样,您的父母将拥有确切的孩子 PID,您可以使用 kill()它。

谷歌搜索“popen2() implementation”以获取一些关于如何实现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;
}

注意:似乎 popen2() 是你想要的,但我的发行版似乎没有这种方法。

于 2009-02-13T23:54:01.533 回答
7

这是 popen2 的改进版本(感谢 Sergey L.)。slacy 发布的版本并没有返回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};
于 2012-10-08T19:26:05.150 回答
4

popen 实际上并不启动线程,而是分叉一个进程。当我查看定义时,似乎没有一种简单的方法可以获取该进程的 PID 并杀死它。可能有一些困难的方法,比如检查进程树,但我想你最好使用 pipe、fork 和 exec 函数来模仿 popen 的行为。然后您可以使用从 fork() 获得的 PID 来终止子进程。

于 2009-02-13T23:28:08.697 回答
3

实际上,如果进程正在执行 I/O(它应该是,否则为什么popen而不是system(3)?),那么pclose应该SIGPIPE在下次尝试读取或写入时用 a 重击它,它应该很好地倒下:-)

于 2009-02-13T23:57:55.783 回答
1

我遵循@slacy 创建函数popen2 的想法。
但是当子进程死亡时发现问题,仍然读取文件描述符的父进程outfp没有从函数返回。

这可以通过在父进程上添加关闭的未使用管道来解决。

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

我们可以通过使用 bash 作为 shell 来获得新进程的正确 pid。

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

当子进程死亡时,函数的调用者popen2应该通过 call 收集子进程的状态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);
}
于 2013-12-20T13:48:19.957 回答
0

显而易见的方法是 system("pkill process_name");

显然,如果您有多个正在运行的进程实例,这是有问题的。

于 2009-02-13T23:27:39.383 回答
0

我在 python 中做了类似的事情,您可以在其中杀死一个子进程以及为此派生的任何子进程。它实际上类似于 slacy 的解决方案。 http://www.pixelbeat.org/libs/subProcess.py

于 2009-02-13T23:58:20.387 回答
0

我只是在第一行放入(bash)脚本:

echo PID $$

然后我读了 pid,并用它来做一个:kill(pid,9)

于 2015-01-07T10:23:36.000 回答