【问题标题】:why the pid of process which is created with fork() and exec() finally changed为什么用 fork() 和 exec() 创建的进程的 pid 最终改变了
【发布时间】:2018-08-28 16:49:52
【问题描述】:

我用fork和exec启动一个进程,但是当我使用ps比如ps afx | grep sublime查找pid时,我发现这两个pid(一个是fork()返回值,另一个是@987654327 @result) 不同。

我的代码:

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

方法一的结果

方法2的结果

我感到很困惑,因为我认为,在孩子身上,execv() 的使用是无关紧要的;这不会改变 pid。

【问题讨论】:

  • 看起来sublime_text 在内部使用fork()

标签: linux


【解决方案1】:

看起来@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 +++

在这里我们可以看到问题中描述的 pid。请注意来自 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,您会发现 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 时,shell 将简单地调用 exec 系列函数之一来用可执行文件替换自己......但是,如果 shell is 附加到 TTY,它将首先通过另一个fork 呼叫。

POSIX spec 中有很多非常好的信息,但可能需要多次阅读才能真正深入了解......此外,查看 POSIX 操作系统的源代码并尝试了解进程管理部分将非常有帮助巩固认识。我是用 QNX neutrino 做的,但 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

...我变得懒惰并使用了预处理器,理想情况下(如果这是您想要保留的工具的开始),那么您需要解析 mainargv 来告诉您哪种方法使用,以及在哪里可以找到可执行文件/为可执行文件提供参数。我把它作为练习留给读者;-)

【讨论】:

  • 如果我需要用 fork 和 exec 启动进程,我能得到什么 pid?搜索 /proc 还是有其他简单/高效的方法?
  • 您应该可以使用ptrace() 来跟踪系统调用(我还没有这样做,但是This Article 经历了一些细节(也可以查看man 2 ptrace);如果您能掌握并使用代码,然后您应该能够获取子 pid。
  • 否则,似乎使用 proc 是要走的路……有一些源 here 打印一个进程树,检查定义 struct Proc 然后打开 /proc/${pid}/status (其中${pid}` 是 sublime 进程的 pid),然后将内容复制到 Proc 结构中并查找子进程。 (免责声明:我还没有在 linux 上做过这个,所以可能需要一些实验)
猜你喜欢
  • 2011-07-02
  • 2017-05-19
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2012-08-02
  • 1970-01-01
  • 2017-02-25
  • 2017-02-14
相关资源
最近更新 更多