#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int createproc();
pid_t pid;
int main()
{
createproc();
printf("%d\n", pid);
exit(0);//_exit(0) gives the same result
}
int createproc()
{
if(!(pid=vfork())) {
printf("child proc:%d\n", pid);
}
else
printf("parent proc:%d\n", pid);
}
the output of the program is below:
child proc:0
0
parent proc:6958
child proc:0
Segmentation fault
As I know, vfork will suspend the parent process unless the exec or exit function is called and the stack segment is shared. So Here I have two questions:
Since they share a common address space, does exit(0) affect both process? If so, how? If not, why?
Why there is a line of "child proc:0" after "parent proc:6958"? I don't expect an answer like unexpected behavior.
Besides, through disassemble, I notice that the call of vfork didn't behave as normal function. There is no stack balance: Dump of assembler code for function vfork:
0xb7ed2050 <+0>: pop ecx
=> 0xb7ed2051 <+1>: mov edx,DWORD PTR gs:0x6c
0xb7ed2058 <+8>: mov eax,edx
0xb7ed205a <+10>: neg eax
0xb7ed205c <+12>: jne 0xb7ed2063 <vfork+19>
0xb7ed205e <+14>: mov eax,0x80000000
0xb7ed2063 <+19>: mov gs:0x6c,eax
0xb7ed2069 <+25>: mov eax,0xbe
0xb7ed206e <+30>: int 0x80
0xb7ed2070 <+32>: push ecx
0xb7ed2071 <+33>: test eax,eax
0xb7ed2073 <+35>: je 0xb7ed207c <vfork+44>
0xb7ed2075 <+37>: mov DWORD PTR gs:0x6c,edx
0xb7ed207c <+44>: cmp eax,0xfffff001
0xb7ed2081 <+49>: jae 0xb7ed2084 <vfork+52>
0xb7ed2083 <+51>: ret
0xb7ed2084 <+52>: call 0xb7f44d87 <__i686.get_pc_thunk.cx>
0xb7ed2089 <+57>: add ecx,0xedf77
0xb7ed208f <+63>: mov ecx,DWORD PTR [ecx-0x104]
0xb7ed2095 <+69>: xor edx,edx
0xb7ed2097 <+71>: sub edx,eax
0xb7ed2099 <+73>: add ecx,DWORD PTR gs:0x0
0xb7ed20a0 <+80>: mov DWORD PTR [ecx],edx
0xb7ed20a2 <+82>: or eax,0xffffffff
0xb7ed20a5 <+85>: jmp 0xb7ed2083 <vfork+51>
It actually pop the return address into ecx and push back after the system call(0xb7ed206e <+30>: int 0x80 0xb7ed2070 <+32>: push ecx). With the most unusual thing that there is a ret instruction: 0xb7ed2083 <+51>: ret
I am not familiar with assemble language, can anyone explain it to me?
The only things you're allowed to do in the child process after vfork
is:
_exit
exec
.Absolutely nothing else. If you intend to do anything else (and printf
is probably one of the worst functions you can call inside vfork
and definitely counts as "anything else"), do not use vfork
, use fork
instead.
The reason for this is that in old times the fork
system call could be pretty slow when it copied the whole address space of the process and in most cases fork
was immediately followed by exec*
which threw that address space away. So vfork
was invented that instead of copying the address space, the new process borrows the address space of the parent process and suspends it until exit
or exec
is called.
So what your code does is first it does memory allocations for printf buffers in the child process (but the address space of the parent). This may or may not be unsafe and confuse the parent. Then the createproc
function returns possibly overwriting the stack frame that the parent will use when it comes back, then it calls printf
again, this time definitely destroying the stack frame and further confusing the internals of printf, then it returns from main
which flushes the printf buffers, destroys the stack frame that main
in the parent used, probably frees a lot of state and stdio state that's necessary for printf to work, then it exits. That exit unsuspends the parent, which probably crashes because the stack frame it returns to is already wrecked. If it isn't wrecked and somehow it manages to call printf, the internal state in printf is wrecked and if that isn't wrecked, the stdout descriptor has already been freed by the child and that definitely crashes.
In other words, if the only thing in your code that runs in the child isn't _exit
or exec
, then your code has no chance of working because that's not something that vfork
can handle.