zyzhang
zyzhang

Reputation: 75

why the pid of process which is created with fork() and exec() finally changed

I start a process with fork and exec,but when I use ps like ps afx | grep sublime to look up the pid, I found that these two pids(one is fork() return value,the other is the ps result) are different.

my code :

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

the result of method 1

enter image description here

the result of method 2

enter image description here

I feel very confused,because I think,in the child, the use of execv() is irrelevant; that doesn't change the pid.

Upvotes: 1

Views: 437

Answers (1)

Ch4ni
Ch4ni

Reputation: 312

It looks like @Barmar is correct here ... internally sublime text is creating one (well ... there is definitely more than one) child here ... most likely with fork(). You can tell from the clone call below that sublime is creating children.

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

And here we can see the pids, as described in the question. Please note the child_tidptr value from strace: it corresponds with the actual PID of sublime, rather than

[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]$ 

if you were to use something a little simpler, like sleep for example, you would find that the pids line up with your expectations:

[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

or, using method 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

An interesting point to note, is that you use "/bin/sh -c" in method 2 ... this step shouldn't be required. IIRC, when not attached to a tty the shell will simply call one of the exec family of functions to replace itself with the executable ... if the shell is attached to a TTY, though, it will go through another fork call first.

There's lots of really good information in the POSIX spec, but it may take several readings to really sink in ... also, checking out the source code of a POSIX OS and trying to understand the process management pieces will really help solidify the understanding. I did this with QNX neutrino, but FreeBSD is another really good one to check out.

For this exercise, I modified your main() function a little bit, to be easier to use:

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

which can be compiled like so:

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

... I got lazy and used the preprocessor, ideally (if this were the start of a tool you want to keep around), then you would want to parse main's argv to tell you which method to use, and where to find the executable/provide args for the executable. I leave that as an exercise for the reader ;-)

Upvotes: 1

Related Questions