Reputation: 75
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
the result of method 2
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
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