cygwin关键技术:fork

来源:互联网 发布:windows server 2000 编辑:程序博客网 时间:2024/06/07 02:24

快乐虾

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

lights@hb165.com

 

本文适用于

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)

 

 

 

 

 

原创粉丝点击