快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Cygwin checkout-2008-09-28
vs2008
欢迎转载,但请保留作者信息
cygwin对fork的模拟大概是最难理解的了,因为它涉及的东西比较多,本文尝试对其进行一点简单的探讨。
fork函数的实现在fork.cc文件中,如果单看这个函数感觉逻辑还是比较简单的:
CYG_API int fork ()
{
frok grouped;
……………..
ischild = !!setjmp (grouped.ch.jmp);
volatile char * volatile var_esp;
//__asm__ volatile ("movl %%esp,%0": "=r" (esp));
__asm
{
mov var_esp, esp;
}
if (!ischild)
res = grouped.parent (var_esp);
else
{
res = grouped.child (var_esp);
ischild = true; /* might have been reset by fork mem copy */
}
………………..
}
对于父进程,将调用grouped.parent函数,注意在调用函数之前传递了当前的ESP指针。
Cygwin实际创建子进程的工作将由grouped.parent完成,且看看它做了什么:
int __stdcall
frok::parent (volatile char * volatile stack_here)
{
………………….
forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
………….
ch.forker_finished = forker_finished;
ch.stackbottom = (char*)&_my_tls + CYGTLS_PADSIZE;//_tlsbase;
ch.stacktop = (void *) stack_here;
ch.stacksize = (char *) ch.stackbottom - (char *) stack_here;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
memset (&si, 0, sizeof (si));
si.cb = sizeof si;
si.lpReserved2 = (LPBYTE) &ch;
si.cbReserved2 = sizeof (ch);
/* FIXME: myself->progname should be converted to WCHAR. */
tmp_pathbuf tp;
PWCHAR progname = tp.w_get ();
sys_mbstowcs (progname, NT_MAX_PATH, myself->progname);
syscall_printf ("CreateProcess (%W, %W, 0, 0, 1, %p, 0, 0, %p, %p)",
progname, progname, c_flags, &si, &pi);
bool locked = __malloc_lock ();
time_t start_time = time (NULL);
/* Remove impersonation */
cygheap->user.deimpersonate ();
fix_impersonation = true;
while (1)
{
rc = CreateProcessW (progname, /* image to run */
progname, /* what we send in arg0 */
&sec_none_nih,
&sec_none_nih,
TRUE, /* inherit handles from parent */
c_flags,
NULL, /* environment filled in later */
0, /* use current drive/directory */
&si,
&pi);
if (!rc)
{
this_errno = geterrno_from_win_error ();
error = "CreateProcessW failed";
memset (&pi, 0, sizeof (pi));
goto cleanup;
}
CloseHandle (pi.hThread);
/* Protect the handle but name it similarly to the way it will
be called in subproc handling. */
ProtectHandle1 (pi.hProcess, childhProc);
strace.write_childpid (ch, pi.dwProcessId);
/* Wait for subproc to initialize itself. */
if (!ch.sync (pi.dwProcessId, pi.hProcess, FORK_WAIT_TIMEOUT))
{
…………….
}
break;
}
…………….
}
可以看到,cygwin最终也是通过CreateProcessW来创建子进程的,这也符合我们对它的预期。这里需要注意的是传递给子进程的参数ch,它是frok类里的一个公有成员:
child_info_fork ch;
还有一点需要注意的是这个参数传递给子进程时,它是位于父进程的栈上的。
当CreateProcessW创建子进程时,它将加载cygwin.dll,根据vc crt的执行顺序,它将首先执行一些全局变量的构造函数,然后从DllMain开始执行,cygwin为了模拟出fork的效果,就在这里进行了一些特殊处理。
和父进程一样,子进程也将执行cygwin里的dll_crt0_0函数:
void __stdcall
dll_crt0_0 ()
{
……………..
child_proc_info = get_cygwin_startup_info ();
if (!child_proc_info)
memory_init ();
else
{
cygwin_user_h = child_proc_info->user_h;
switch (child_proc_info->type)
{
case _PROC_FORK:
fork_info->handle_fork ();
break;
case _PROC_SPAWN:
case _PROC_EXEC:
spawn_info->handle_spawn ();
break;
}
}
……………………
}
在这里,父子进程将取得不同的child_proc_info,对于父进程而言,这个指针将为NULL,而子进程将得到一个描述进程信息的指针。看看get_cygwin_startup_info做了什么:
child_info *
get_cygwin_startup_info ()
{
STARTUPINFO si;
GetStartupInfo (&si);
child_info *res = (child_info *) si.lpReserved2;
if (si.cbReserved2 < EXEC_MAGIC_SIZE || !res
|| res->intro != PROC_MAGIC_GENERIC || res->magic != CHILD_INFO_MAGIC)
res = NULL;
else
{
………………
}
return res;
}
它将取出CreateProcess传递过来的child_info对象并返回,而对于父进程,crt传递进来的si.lpReserved2为将为NULL。
cygwin关键技术:设备模拟(2009-9-4)
cygwin关键技术:cygheap(2009-9-2)
cygwin关键技术:tls(2009-8-24)
在vs2008下使用cygwin(23):stdin,stdout和stderr(2008-10-21)