【问题标题】:Killing process that has been created with popen2杀死使用 popen2 创建的进程
【发布时间】:2012-10-08 09:17:34
【问题描述】:

我正在使用函数 popen2(已在 stackoverflow 的其他地方推荐)以编程方式创建一个必须在一段时间后再次终止的进程。 popen2 返回一个 PID,我认为这个 PID 可以用来杀死进程。但是,这种方式行不通。为了杀死它,我必须增加返回的 PID,我不明白(见下面的代码)

当多个线程并行执行此操作时,可能会出现另一个问题。在这种情况下,我认为,由于竞争条件,PID 的差异可能不止一个。

所以我的问题是:如何在多线程场景中可靠地识别 popen2 创建的进程的 PID?

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#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;
}

main() {
    pid_t pid;

    // Create process
    pid = popen2("crafty", &in, &out);

    sleep(5);

    // Why doesn't kill(pid, SIGKILL) work?
    kill(pid+1, SIGKILL);

    while (1); 
}

【问题讨论】:

  • 你确定 popen2 实际上返回了一个有效的 pid 吗?它可以返回-1 以指示错误。如果您在-1 上调用kill,您会尝试在系统范围内杀死,这可能会或可能不会有任何好处,相反,0 上的kill 将杀死您的进程组:您的进程和它的子进程。跨度>
  • 是的,我确信返回的 PID 是有效的。

标签: c process exec popen


【解决方案1】:

我想我明白了。

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

运行 shpopen2 返回它的 pid。当您调用kill 时,它会杀死sh,但不会触及它的子进程command。杀死下一个pid杀死command实际上是侥幸。这并不总是有效,取决于竞争条件。

如果您希望能够杀死您的目标进程,那么您必须直接启动它。

警告(未经测试的代码):

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};

在您的特定示例中:

char *command[] = {"crafty", NULL};

【讨论】:

  • 这有点帮助。它按预期工作,但是当我使用返回的 PID 终止进程并执行 ps aux | grep 在shell 上狡猾,我看到该进程仍在运行。然而,它是。在程序本身终止之前它不会消失。
  • 那是因为死进程需要由它的父进程收集/清理。当子进程死亡时,它会变成僵尸,ps 将标记它为Z&lt;defunct&gt;。一个垂死的进程向它的父进程发送一个SIGCHLD 信号,并且可以通过wait 系列系统调用来收集。 Shell 会自动执行此操作,而用户程序则不会。一个简单的解决方案是在杀死后调用wait(0);(注意:如果孩子因任何原因没有死,将永远等待)。阅读en.wikipedia.org/wiki/Zombie_process 了解有关僵尸进程的更多信息。
【解决方案2】:

您可以使用 shell 的 'exec' 命令来避免挂起的进程。另外:popen2 应关闭未使用管道的写入端,否则管道保持打开状态。如果指针(infp、outpf)之一为 NULL,则创建并立即关闭管道是没有用的。这是我在项目中使用的 popen2 版本:

pid_t popen2(char *command, int *in_fd, int *out_fd) {
    int     pin[2], pout[2];
    pid_t   pid;
    char    cmd[strlen(command)+10];

    if (out_fd != NULL) {
        if (pipe(pin) != 0) return(-1);
    }
    if (in_fd != NULL) {
        if (pipe(pout) != 0) {
            if (out_fd != NULL) {
                close(pin[0]);
                close(pin[1]);
            }
            return(-1);
        }
    }

    pid = fork();

    if (pid < 0) {
        if (out_fd != NULL) {
            close(pin[0]);
            close(pin[1]);
        }
        if (in_fd != NULL) {
            close(pout[0]);
            close(pout[1]);
        }
        return pid;
    }
    if (pid==0) {
        if (out_fd != NULL) {
            close(pin[1]);
            dup2(pin[0], 0);
        }
        if (in_fd != NULL) {
            close(pout[0]);
            dup2(pout[1], 1);
        }
        // Exec makes possible to kill this process 
        sprintf(cmd, "exec %s", command);
        execlp("sh", "sh", "-c", cmd, NULL);
        fprintf(stderr, "%s:%d: Exec failed in popen2. ", __FILE__, __LINE__);
        perror("Error:");
        exit(1);
    }
    if (in_fd != NULL) {
        close(pout[1]);
        *in_fd = pout[0];
    }
    if (out_fd != NULL) {
        close(pin[0]);
        *out_fd = pin[1];
    }
    return pid;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 2010-09-28
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    相关资源
    最近更新 更多