简体   繁体   中英

subprocess.popen (python) in C

Like the question says, I am searching for the subprocess.open module in C

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, preexec_fn=os.setsid)
print p.pid

I am trying to spawn a process and then get the process ID.

`cmd` is a c program that is spawned

In C, I believe that the getpid() does the job.

But after I spawn the process, I don't know how to tell getpid() to get that spawned process, pid.

void run_apps(pid) {
        printf("process ID is %d\n", (int) getpid());
}

That is obviously giving the current process ID.

In C, you get the maximum number of options by first calling fork() to create a new process (the eventual subprocess), then one of the exec*() family of functions to execute the subprocess. Both the original process and the new process will run concurrently, so you can exchange (read and/or write data) via pipes or socket pairs. Finally, use eg waitpid() in a loop to wait for the new process to exit, and "reap" its exit status. For example:

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

int main(void)
{
    pid_t child, p;
    int   status;

    /*
     * Prepare pipes et cetera first.
    */

    /* Fork to create the subprocess. */
    child = fork();
    if (child == (pid_t)-1) {
        /* Cannot fork(); usually out of resources (user limits).
         * see errno for details. With <string.h>, you can use
         * strerror(errno) to obtain the error string itself. */
        return 1;

    } else
    if (!child) {
        /* This is the child process itself.
         * Do whatever cleanup is necessary, then
         * execute the subprocess command. */
        execlp("/bin/ls",  "ls", "-lA", NULL);

        /* This is only reached if the exec failed;
         * again, see errno for reason.
         * Always have the child process exit! */
        return 127;
    }

    /* This is only run by the parent process
     * (because the child always exits within the
     *  else if body above).
     *
     * The subprocess PID is 'child'.
    */

    /* Wait for the child process to exit. */
    do {
        status = 0;
        p = waitpid(child, &status, 0);
        if (p == (pid_t)-1 && errno != EINTR)
            break; /* Error */
    } while (p != child);

    if (p != child) {
        /* Child process was lost.
         * If (p == (pid_t)-1), errno describes the error.
        */

    } else
    if (WIFEXITED(status)) {
        /* Child process exited with WEXITSTATUS(status) status.
         * A status of 0 (or EXIT_SUCCESS) means success,
         * no errors occurred. Nonzero usually means an error,
         * but codes vary from binary to binary.
        */

    } else
    if (WIFSIGNALED(status)) {
        /* Child process died from WTERMSIG(status) signal.
         * If you include <string.h>, you can use
         *     strsignal(WTERMSIG(status))
         * to obtain the name (string) of the terminating signal.
        */

    } else {
        /* Child process died from unknown causes.
        */

    }

    /* All done. */
    return 0;
}

Personally, I prefer to use socketpair() to create Unix domain stream or datagram sockets between processes I control, and pipes created via pipe() if the subprocess is just some random binary to be run. In all cases you can replace the standard input ( STDIN_FILENO descriptor), standard output ( STDOUT_FILENO descriptor), and standard error ( STDERR_FILENO descriptor) with sockets or pipes, using the dup2() function. You can even access the pseudo-files under /proc/[child]/ from the parent to observe the state of the child process if you want.

Depending on how you need to communicate with the subprocess -- input/output from/to files? strings in memory? dynamically allocated buffer for output -- there are many variants. Usually code similar to above is used when precise control and/or full-duplex (both read and write) and/or asynchronous communications are needed.

You can do a search for "linux" "fork" "exec" in your favourite search engine for examples of varying quality.


If you want an easier solution, and you only need to capture the output of the command (supplying no input to the command, or perhaps supplying the input from a file), you could use some variant of

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main(void)
{
    FILE *sub;
    pid_t subpid;
    int   status;

    sub = popen("setsid /bin/sh -c 'echo $$ ; exec command args' </dev/null", "rb");
    if (!sub) {
        /* popen() failed. */
        return 1;
    }

    /* Read the first line from sub. It contains the PID for the command. */
    {
        char buffer[32], *line, dummy;
        int  value;

        line = fgets(buffer, sizeof buffer, sub);
        if (!line) {
            /* setsid failed, or non-POSIXy system (Windows?) */
            pclose(sub);
            return 1;
        }
        if (sscanf(line, "%d%c", &value, &dummy) != 1 || value < 2) {
            /* Internal bug, or extra output? */
            pclose(sub);
            return 1;
        }

        /* subpid is also the session ID and the process group ID,
         * because it is the session leader. */
        subpid = value;
    }

    /* Read from sub using standard I/O, to capture command output. */

    /* After no more output to read from sub, reap the subprocess. */
    errno = 0;
    do {
        status = pclose(sub);
    } while (status == -1 && errno == EINTR);

    if (status) {
        /* Problem: sub exited with nonzero exit status 'status',
         * or if status == -1, some other error occurred. */

    } else {
        /* Sub exited with success (zero exit status). */
    }

    /* Done. */
    return 0;
}

In Linux, popen() uses the /bin/sh shell (as per POSIX.1 specification), and we can use the setsid command-line utility to create the new session. Within the command, the echo $$ is a sh command which outputs the shell PID, and exec CMD... replaces the shell with the command; thus we get the PID of the command even before the command gets executed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM