【问题标题】:subprocess.popen (python) in CC中的subprocess.popen(python)
【发布时间】:2013-06-19 16:47:28
【问题描述】:

正如问题所说,我正在C 中搜索subprocess.open module

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

我正在尝试生成一个进程,然后获取进程 ID。

`cmd` is a c program that is spawned

在 C 语言中,我相信 getpid() 可以完成这项工作。

但是在我生成进程之后,我不知道如何告诉getpid() 来获取生成的进程,pid。

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

这显然是给出当前进程ID。

【问题讨论】:

  • @OregonTrail:但是您不会在那里启动新的外部流程,或者是吗?我是 C.. 的初学者。
  • fork 是生成进程的底层机制。它复制当前进程。 exec 是你想要的,它复制当前进程,然后用新的二进制可执行文件替换它
  • 好的。我明白了。你的程序中的if (execvp(cmd[0], cmd) < 0) 正在做这项工作,对吧?
  • 没错,我们已经分叉并确定了我们是否处于子进程中。然后我们执行

标签: python c linux


【解决方案1】:

在 C 中,您可以通过首先调用 fork() 创建一个新进程(最终的子进程),然后调用 exec*() 系列函数之一来执行子进程,从而获得最大数量的选项。原始进程和新进程都将同时运行,因此您可以通过管道或套接字对交换(读取和/或写入数据)。最后,使用例如waitpid() 在循环中等待新进程退出,并“收获”其退出状态。例如:

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

就个人而言,如果子进程只是一些要运行的随机二进制文件,我更喜欢使用socketpair() 在我控制的进程之间创建 Unix 域流或数据报套接字,以及通过pipe() 创建的管道。在所有情况下,您都可以使用dup2() 函数将标准输入(STDIN_FILENO 描述符)、标准输出(STDOUT_FILENO 描述符)和标准错误(STDERR_FILENO 描述符)替换为套接字或管道。如果需要,您甚至可以从父进程访问/proc/[child]/ 下的伪文件以观察子进程的状态。

取决于您需要如何与子进程进行通信——从/到文件的输入/输出?内存中的字符串?为输出动态分配缓冲区——有很多变体。当需要精确控制和/或全双工(读写)和/或异步通信时,通常会使用与上述类似的代码。

您可以在自己喜欢的搜索引擎中搜索"linux" "fork" "exec",以获取不同质量的示例。


如果您想要一个更简单的解决方案,并且您只需要捕获命令的输出(不向命令提供输入,或者可能提供来自文件的输入),您可以使用一些变体

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

在 Linux 中,popen() 使用 /bin/sh shell(根据 POSIX.1 规范),我们可以使用 setsid 命令行实用程序来创建新会话。在命令中,echo $$ 是一个输出 shell PID 的sh 命令,exec CMD... 将 shell 替换为命令;因此,我们甚至在命令执行之前就获得了命令的 PID。

【讨论】:

  • 很抱歉删除了之前的评论。他们没有给出新进程的 pid。
  • 例如,我不能说像./mb 1这样的东西,而不是“ls -la”
  • @user2015933:我不明白。我的第一个 sn-p 将在child 中拥有新进程的 pid,第二个 sn-p 在 subpid 中。要执行./mb 1,您可以在第一个sn-p 中使用例如execl("./mb", "./mb", "1", NULL);(第一个参数 是命令名称本身,这就是它“重复”的原因;参见man execl 人页)。在第二个sn-p中,可以使用sub = popen("setsid /bin/sh -c 'echo $$ ; exec ./mb 1' &lt;/dev/null", "rb");
  • 我弄错了。但是,我正在尝试修复一种在执行程序后从 shell 中取回控制权的方法。
猜你喜欢
  • 2015-07-19
  • 1970-01-01
  • 2019-11-10
  • 1970-01-01
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 2014-11-12
  • 2011-07-05
相关资源
最近更新 更多