cygwin关键技术:fork

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

本文适用于

Cygwin checkout-2008-09-28

vs2008

欢迎转载,但请保留作者信息

cygwinfork的模拟大概是最难理解的了,因为它涉及的东西比较多,本文尝试对其进行一点简单的探讨。

1.1 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指针。

1.2 创建子进程之前

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;

还有一点需要注意的是这个参数传递给子进程时,它是位于父进程的栈上的。

1.3 子进程的执行

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

2 参考资料

cygwin关键技术:设备模拟(2009-9-4)

cygwin关键技术cygheap(2009-9-2)

cygwin关键技术:tls(2009-8-24)

vs2008下使用cygwin23):stdinstdoutstderr(2008-10-21)

你可能感兴趣的:(.net,工作,Blog,vc++)