为什么用fork()和exec()创建的进程的pid最终改变了

时间:2018-08-28 16:49:52

标签: linux

我使用fork和exec启动一个进程,但是当我使用ps之类的ps afx | grep sublime查找pid时,我发现这两个pid(一个是fork()返回值,另一个是ps的结果)。

我的代码:

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

int create_process(char *name, char *argv[])
{
    int pid = fork();
    if (0 == pid)
    {
        execv(name, argv);
        exit(127);
    }
    else if (0 < pid)
    {
        return pid;
    }else
    {
        return -1;
    }
}

int forkstyle_system(char *cmdstring)
{
    int pid = fork();
    if (0 == pid)
    {
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        exit(127);
    }
    else if (0 < pid)
    {
        return pid;
    }
    else
    {
        return -1;
    }
}

int main()
{
    //method 1
    char *name = "/opt/sublime_text/sublime_text";
    char *argv[] = {"/opt/sublime_text/sublime_text", (char *)0};
    int pid = create_process(name, argv);
    printf("pid = %d\n",pid);

    //method 2
    /*
    char *cmdstring = "/opt/sublime_text/sublime_text";
    int pd = forkstyle_system(cmdstring);
    printf("pid = %d\n",pd);
    */
    return 0;
}

方法1的结果

enter image description here

方法2的结果

enter image description here

我感到非常困惑,因为我认为在孩子中,execv()的使用是无关紧要的。不会改变pid。

1 个答案:

答案 0 :(得分:0)

@Barmar在这里似乎是正确的……内部崇高的文本在这里创建了一个(好吧……肯定有一个以上)孩子……最​​有可能使用fork()。您可以从下面的clone呼叫中看出,崇高精神正在创造孩子。

[acripps@localhost Code]$ strace -e trace=%process /opt/sublime_text/sublime_text 
execve("/opt/sublime_text/sublime_text", ["/opt/sublime_text/sublime_text"], 
0x7ffff4607370 /* 56 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7fb6fa15b740) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, 
child_tidptr=0x7fb6fa15ba10) = 32653
exit_group(0)                           = ?
+++ exited with 0 +++

在这里我们可以看到问题所描述的pid。请注意,来自strace的child_tidptr值:它与升华的实际PID相对应,而不是

[acripps@localhost Code]$ ps afx | grep sublime
32675 pts/0    S+     0:00  |   |   \_ grep --color=auto sublime
32653 ?        Ssl    0:00  \_ /opt/sublime_text/sublime_text
32670 ?        Sl     0:00      \_ /opt/sublime_text/plugin_host 32653 --auto-shell-env
[acripps@localhost Code]$ 

如果您使用一些简单的方法,例如sleep,您会发现pid与您的期望一致:

[acripps@localhost Code]$ ./exec_m1 
pid = 1696
Press ENTER to continue ...
[acripps@localhost Code]$ ps afx | grep sleep
1696 pts/1    S+     0:00  |   |       \_ /usr/bin/sleep 300
1711 pts/2    S+     0:00  |       \_ grep --color=auto sleep

或使用方法2:

[acripps@localhost Code]$ ./exec_m2
pid = 1774
Press ENTER to continue ...
[acripps@localhost Code]$ ps afx | grep sleep
1774 pts/1    S+     0:00  |   |       \_ /usr/bin/sleep 300
1776 pts/2    S+     0:00  |       \_ grep --color=auto sleep

需要注意的有趣一点是,您在方法2中使用了"/bin/sh -c"……不需要执行此步骤。 IIRC,如果未将其附加到tty,则外壳将简​​单地调用exec函数家族之一,以将其自身替换为可执行文件...如果将外壳 附加至TTY,则,它将首先通过另一个fork调用。

POSIX spec中有很多非常好的信息,但是可能需要花费一些阅读才能真正了解……而且,请检查POSIX OS的源代码并尝试了解过程管理的内容。确实有助于巩固理解。我是用QNX中微子做到这一点的,但是FreeBSD是另一个值得一试的好东西。

对于本练习,我对您的main()函数进行了一些修改,以使其易于使用:

int main()
{
    int pid = 0;

#if METHOD == 1
    //method 1
    char *name = "/usr/bin/sleep";
    char *argv[] = {name, "300", (char *)0};
    pid = create_process(name, argv);
#else
#if METHOD == 2
    //method 2
    char *cmdstring = "/usr/bin/sleep 300";
    pid = forkstyle_system(cmdstring);
#endif
#endif

    printf("pid = %d\n",pid);
    printf("Press ENTER to continue ...");
    getchar();
    return 0;
}

可以这样编译:

gcc -o exec_method1 -DMETHOD=1 exec.c
gcc -o exec_method2 -DMETHOD=2 exec.c

...我很懒惰,并且使用了预处理器,理想情况下(如果这是您想保留的工具的开始),那么您可能想将main的{​​{1}}解析为告诉您使用哪种方法,以及在哪里找到可执行文件/为可执行文件提供args。我把它留给读者作为练习;-)