__fork.c

来自「一个C源代码分析器」· C语言 代码 · 共 567 行 · 第 1/2 页

C
567
字号
/* Copyright (C) 1994 Free Software Foundation, Inc.This file is part of the GNU C Library.The GNU C Library is free software; you can redistribute it and/ormodify it under the terms of the GNU Library General Public License aspublished by the Free Software Foundation; either version 2 of theLicense, or (at your option) any later version.The GNU C Library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with the GNU C Library; see the file COPYING.LIB.  Ifnot, write to the Free Software Foundation, Inc., 675 Mass Ave,Cambridge, MA 02139, USA.  */#include <errno.h>#include <unistd.h>#include <hurd.h>#include <hurd/signal.h>#include <setjmp.h>#include "thread_state.h"#include <sysdep.h>		/* For stack growth direction.  */#include "set-hooks.h"#include <assert.h>#include "hurdmalloc.h"		/* XXX */extern void _hurd_longjmp_thread_state (struct machine_thread_state *,					jmp_buf env, int value);/* Things that want to be locked while forking.  */struct  {    size_t n;    struct mutex *locks[0];  } _hurd_fork_locks;/* Things that want to be called before we fork, to prepare the parent for   task_create, when the new child task will inherit our address space.  */DEFINE_HOOK (_hurd_fork_prepare_hook, (void));/* Things that want to be called when we are forking, with the above all   locked.  They are passed the task port of the child.  The child process   is all set up except for doing proc_child, and has no threads yet.  */DEFINE_HOOK (_hurd_fork_setup_hook, (void));/* Things to be run in the child fork.  */DEFINE_HOOK (_hurd_fork_child_hook, (void));/* Things to be run in the parent fork.  */DEFINE_HOOK (_hurd_fork_parent_hook, (void));/* Clone the calling process, creating an exact copy.   Return -1 for errors, 0 to the new process,   and the process ID of the new process to the old process.  */pid_t__fork (void){  jmp_buf env;  pid_t pid;  size_t i;  error_t err;  thread_t thread_self = __mach_thread_self ();  struct hurd_sigstate *volatile ss;  void unlockss (void)    {      ss->critical_section = 0;      /* XXX Copying mutex into child and calling mutex_unlock lossy.  */      __mutex_unlock (&ss->lock);      __mutex_unlock (&_hurd_siglock);      ss = NULL;		/* Make sure we crash if we use it again.  */    }  ss = _hurd_self_sigstate ();  ss->critical_section = 1;  __mutex_lock (&_hurd_siglock);  if (! setjmp (env))    {      process_t newproc;      task_t newtask;      thread_t thread, sigthread;      mach_port_urefs_t thread_refs, sigthread_refs;      struct machine_thread_state state;      unsigned int statecount;      mach_port_t *portnames = NULL;      unsigned int nportnames = 0;      mach_port_type_t *porttypes = NULL;      unsigned int nporttypes = 0;      thread_t *threads = NULL;      unsigned int nthreads = 0;      int ports_locked = 0;      /* Run things that prepare for forking before we create the task.  */      RUN_HOOK (_hurd_fork_prepare_hook, ());      /* Lock things that want to be locked before we fork.  */      for (i = 0; i < _hurd_fork_locks.n; ++i)	__mutex_lock (_hurd_fork_locks.locks[i]);            newtask = MACH_PORT_NULL;      thread = sigthread = MACH_PORT_NULL;      newproc = MACH_PORT_NULL;      /* Lock all the port cells for the standard ports while we copy the	 address space.  We want to insert all the send rights into the	 child with the same names.  */      for (i = 0; i < _hurd_nports; ++i)	__spin_lock (&_hurd_ports[i].lock);      ports_locked = 1;      /* Create the child task.  It will inherit a copy of our memory.  */      if (err = __task_create (__mach_task_self (), 1, &newtask))	goto lose;      /* Fetch the names of all ports used in this task.  */      if (err = __mach_port_names (__mach_task_self (),				   &portnames, &nportnames,				   &porttypes, &nporttypes))	goto lose;      if (nportnames != nporttypes)	{	  err = EGRATUITOUS;	  goto lose;	}      /* Get send rights for all the threads in this task.	 We want to avoid giving these rights to the child.  */      if (err = __task_threads (__mach_task_self (), &threads, &nthreads))	goto lose;      /* Get the child process's proc server port.  We will insert it into	 the child with the same name as we use for our own proc server	 port; and we will need it to set the child's message port.  */      if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,				  newtask, &newproc))	goto lose;      /* Insert all our port rights into the child task.  */      thread_refs = sigthread_refs = 0;      for (i = 0; i < nportnames; ++i)	{	  if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)	    {	      /* This is a receive right.  We want to give the child task		 its own new receive right under the same name.  */	      err = __mach_port_allocate_name (newtask,					       MACH_PORT_RIGHT_RECEIVE,					       portnames[i]);	      if (err == KERN_NAME_EXISTS)		{		  /* It already has a right under this name (?!).  Well,		     there is this bizarre old Mach IPC feature (in #ifdef		     MACH_IPC_COMPAT in the ukernel) which results in new		     tasks getting a new receive right for task special		     port number 2.  What else might be going on I'm not		     sure.  So let's check.  */#if !MACH_IPC_COMPAT#define TASK_NOTIFY_PORT 2#endif		  assert (({ mach_port_t thisport, notify_port;			     mach_msg_type_name_t poly;			     (__task_get_special_port (newtask,						       TASK_NOTIFY_PORT,						       &notify_port) == 0 &&			      __mach_port_extract_right 			      (newtask,			       portnames[i],			       MACH_MSG_TYPE_MAKE_SEND,			       &thisport, &poly) == 0 &&			      (thisport == notify_port) &&			      __mach_port_deallocate (__mach_task_self (),						      thisport) == 0 &&			      __mach_port_deallocate (__mach_task_self (),						      notify_port) == 0);			   }));		}	      else if (err)		goto lose;	      if (porttypes[i] & MACH_PORT_TYPE_SEND)		{		  /* Give the child as many send rights for its receive		     right as we have for ours.  */		  mach_port_urefs_t refs;		  mach_port_t port;		  mach_msg_type_name_t poly;		  if (err = __mach_port_get_refs (__mach_task_self (),						  portnames[i],						  MACH_PORT_RIGHT_SEND,						  &refs))		    goto lose;		  if (err = __mach_port_extract_right (newtask,						       portnames[i],						       MACH_MSG_TYPE_MAKE_SEND,						       &port, &poly))		    goto lose;		  if (portnames[i] == _hurd_msgport)		    {		      /* We just created a receive right for the child's			 message port and are about to insert send rights			 for it.  Now, while we happen to have a send right			 for it, give it to the proc server.  */		      mach_port_t old;		      if (err = __proc_setmsgport (newproc, port, &old))			goto lose;		      if (old != MACH_PORT_NULL)			/* XXX what to do here? */			__mach_port_deallocate (__mach_task_self (), old);		    }		  if (err = __mach_port_insert_right (newtask,						      portnames[i],						      port,						      MACH_MSG_TYPE_MOVE_SEND))		    goto lose;		  if (refs > 1 &&		      (err = __mach_port_mod_refs (newtask,						   portnames[i],						   MACH_PORT_RIGHT_SEND,						   refs - 1)))		    goto lose;		}	      if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)		{		  /* Give the child a send-once right for its receive right,		     since we have one for ours.  */		  mach_port_t port;		  mach_msg_type_name_t poly;		  if (err = __mach_port_extract_right		      (newtask,		       portnames[i],		       MACH_MSG_TYPE_MAKE_SEND_ONCE,		       &port, &poly))		    goto lose;		  if (err = __mach_port_insert_right		      (newtask,		       portnames[i], port,		       MACH_MSG_TYPE_MOVE_SEND_ONCE))		    goto lose;		}	    }	  else if (porttypes[i] & MACH_PORT_TYPE_SEND)	    {	      /* This is a send right or a dead name.		 Give the child as many references for it as we have.  */	      mach_port_urefs_t refs, *record_refs = NULL;	      mach_port_t insert;	      if (portnames[i] == newtask)		/* Skip the name we use for the child's task port.  */		continue;	      if (portnames[i] == __mach_task_self ())		/* For the name we use for our own task port,		   insert the child's task port instead.  */		insert = newtask;	      else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)		{		  /* Get the proc server port for the new task.  */		  if (err = __proc_task2proc (portnames[i], newtask, &insert))		    goto lose;		}	      else if (portnames[i] == thread_self)		{		  /* For the name we use for our own thread port, we will		     insert the thread port for the child main user thread		     after we create it.  */		  insert = MACH_PORT_NULL;		  record_refs = &thread_refs;		  /* Allocate a dead name right for this name as a                     placeholder, so the kernel will not chose this name                     for any other new port (it might use it for one of the                     rights created when a thread is created).  */		  if (err = __mach_port_allocate_name		      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))		    goto lose;		}	      else if (portnames[i] == _hurd_msgport_thread)		/* For the name we use for our signal thread's thread port,		   we will insert the thread port for the child's signal		   thread after we create it.  */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?