Killing process that has been created with popen2

2019-02-21 02:06发布

I'm using the function popen2 (that has been recommended elsewhere on stackoverflow) to programatically create a process that has to be killed again after some time. popen2 returns a PID and I thought that this PID could be used to kill the process. It doesn't work this way, though. In order to kill it, I have to increment the returned PID, which I don't understand (see code below)

Another problem might arise when various threads are doing this in parallel. In that case the PIDs might differ by more than one due to race conditions, I think.

So my question is this: How can I reliably identify the PID of the process created by popen2 in a multi-threaded scenario?

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

2条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-21 02:21

I think I got it.

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

runs sh and popen2 returns it's pid. When you call kill it kills sh, but does not touch it's child process command. It is actually a fluke that killing the next pid kills command. This will not always work and is just up to race conditions.

If you want to be able to kill your target process then you will have to start that directly.

Warning (untested code):

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

and pass command in the form of

char *command[] = {"program", "arg1", "arg2", ..., NULL};

in your particular example:

char *command[] = {"crafty", NULL};
查看更多
来,给爷笑一个
3楼-- · 2019-02-21 02:25

You can use 'exec' command of shell to avoid pending process. Also: popen2 shall close the writing end of unused pipes, otherwise the pipe remains open. If one of pointers (infp, outpf) is NULL, it is useless to create and immediately close the pipe. Here is the version of popen2 I use in my project:

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;
}
查看更多
登录 后发表回答