我用fork和exec启动一个进程,但是当我使用pslikeps 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的结果
linux - 为什么用fork()和exec()创建的进程的pid最终改变了-LMLPHP
方法2的结果
linux - 为什么用fork()和exec()创建的进程的pid最终改变了-LMLPHP
我感到非常困惑,因为我认为,在孩子身上,execv()的使用是不相关的;这不会改变pid。

最佳答案

看起来,@barmar在这里是正确的…内部崇高的文本正在创造一个(嗯…这里肯定不止一个孩子……最有可能是fork()。从下面的clone调用可以看出sublime正在创建子级。

[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 +++

这里我们可以看到问题中描述的pids。请注意strace的child_tidptr值:它对应于sublime的实际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,你会发现pids符合你的期望:
[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时,shell只需调用exec函数家族中的一个函数,用可执行文件替换它自己……不过,如果shell连接到tty,它将首先通过另一个fork调用。
POSIX spec中有很多非常好的信息,但是可能需要几次阅读才能真正了解……此外,检查posix操作系统的源代码并尝试理解流程管理部分将真正有助于巩固理解。我用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'sargv来告诉您要使用哪种方法,以及在哪里找到可执行文件/为可执行文件提供参数。我把它留给读者作为练习;-)

关于linux - 为什么用fork()和exec()创建的进程的pid最终改变了,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52062862/

10-12 17:38
查看更多