调用vfork后的奇怪输出 #包括 #包括 #包括 #包括 int createproc(); pid_t pid; int main() { createproc(); printf(“%d\n”,pid); 退出(0);//_退出(0)给出相同的结果 } int createproc() { 如果(!(pid=vWork()){ printf(“子进程:%d\n”,pid); } 其他的 printf(“父进程:%d\n”,pid); }
程序的输出如下: 子进程:0 0 父进程:6958 子进程:0 分段故障 我知道,vWork将挂起父进程,除非调用exec或exit函数并共享堆栈段。 所以我有两个问题:调用vfork后的奇怪输出 #包括 #包括 #包括 #包括 int createproc(); pid_t pid; int main() { createproc(); printf(“%d\n”,pid); 退出(0);//_退出(0)给出相同的结果 } int createproc() { 如果(!(pid=vWork()){ printf(“子进程:%d\n”,pid); } 其他的 printf(“父进程:%d\n”,pid); },c,linux,disassembly,vfork,C,Linux,Disassembly,Vfork,程序的输出如下: 子进程:0 0 父进程:6958 子进程:0 分段故障 我知道,vWork将挂起父进程,除非调用exec或exit函数并共享堆栈段。 所以我有两个问题: 由于它们共享一个公共地址空间,出口(0)是否会影响这两个进程?如果是,怎么做?若否,原因为何 为什么在“父进程:6958”之后有一行“子进程:0”?我不希望得到像意外行为这样的回答 此外,通过反汇编,我注意到vfork的调用没有正常的功能。没有堆栈平衡: 函数vfork的汇编程序代码转储: #include <sys/t
#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);
}
0xb7ed2050:pop ecx
=>0xb7ed2051:mov edx,DWORD PTR gs:0x6c
0xb7ed2058:mov eax,edx
0xb7ed205a:neg eax
0xb7ed205c:jne 0xb7ed2063
0xb7ed205e:mov eax,0x8000000
0xb7ed2063:mov gs:0x6c,eax
0xb7ed2069:mov eax,0xbe
0xb7ed206e:int 0x80
0xb7ed2070:推送ecx
0xb7ed2071:测试eax,eax
0xb7ed2073:je 0xb7ed207c
0xb7ed2075:mov DWORD PTR gs:0x6c,edx
0xb7ed207c:cmp eax,0xfffff001
0xb7ed2081:jae 0xb7ed2084
0xb7ed2083:ret
0xb7ed2084:调用0xb7f44d87
0xb7ed2089:添加ecx,0xedf77
0xb7ed208f:mov ecx,DWORD PTR[ecx-0x104]
0xb7ed2095:xor edx,edx
0xb7ed2097:子edx,eax
0xb7ed2099:添加ecx,DWORD PTR gs:0x0
0xb7ed20a0:mov DWORD PTR[ecx],edx
0xb7ed20a2:或eax,0xffffffff
0xb7ed20a5:jmp 0xb7ed2083
实际上,它将返回地址弹出到ecx中,并在系统调用后回推(0xb7ed206e:int 0x80 0xb7ed2070:push ecx)。最不寻常的是有一条ret指令:0xb7ed2083:ret
我不熟悉汇编语言,有人能给我解释一下吗?我只能引用
人的工作:
(来自POSIX.1)vfork()函数与fork(2)具有相同的效果,
但是,如果vfork()创建的进程修改除用于存储数据的pid类型变量以外的任何数据,则该行为是未定义的
从vfork()返回值,或从调用vfork()的函数返回值,或在成功调用_exit(2)或exec(3)系列函数之一之前调用任何其他函数
这里的重要部分是:
如果在调用\u exit
因此,请修改您的代码:
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>
避免未定义的行为。因此,您必须退出应用程序。(是的,6只是一个值)
当然,如果您希望调用exec
系列中的函数(即:运行另一个应用程序),也可以调用该系列中的函数。在vWork
之后,在子进程中只允许执行以下操作:
- 将函数的返回值存储到变量中
- 呼叫
\u退出
- 调用操作系统提供的某些函数,该函数的名称以
exec
开头
绝对没有别的了。如果您打算执行其他操作(并且printf
可能是您可以在vfork
内部调用的最差函数之一,并且肯定被视为“其他操作”),请不要使用vfork
,而是使用fork
这是因为在旧时代,fork
系统调用在复制进程的整个地址空间时可能会非常慢,在大多数情况下,fork
后面紧跟着exec*
,这就把地址空间扔掉了。因此vfork
被发明出来,新进程不复制地址空间,而是借用父进程的地址空间并将其挂起,直到调用exit
或exec
因此,您的代码首先为子进程中的printf缓冲区(但父进程的地址空间)分配内存。这可能不安全,也可能不安全,并使家长感到困惑。然后,createproc
函数返回时可能会覆盖父级返回时将使用的堆栈帧,然后再次调用printf
,这一次肯定会破坏堆栈帧并进一步混淆printf的内部,然后从刷新printf缓冲区的main
返回,销毁所用父级中main
的堆栈框架,可能会释放printf工作所需的大量状态和stdio状态,然后退出。该出口解除父级的挂起,这可能会崩溃,因为它返回的堆栈帧已经损坏。如果它没有崩溃,并且以某种方式调用printf,printf中的内部状态就会崩溃,如果没有崩溃,则子系统已经释放了stdout描述符,这肯定会崩溃
换句话说,如果在子级中运行的代码中唯一的东西不是\u exit
或exec
,那么您的代码就没有工作的机会,因为这不是vfork
可以处理的。此代码仍然未定义,因为您正在调用“printf”。实际上,这是一个函数,而不是_exit或exec*@如果你有一个好的观点,我只是假设printf不会修改变量,但你永远不知道…@fritzone,printf是否修改并不重要
int createproc()
{
if(!(pid=vfork())) {
_exit(6);
}
else
printf("parent proc:%d\n", pid);
}