继续看一下 glibc 的代码. 继续内存同步这一块. 实际是 pthreads 相关函数.

PS: 好像很多实现都在 hurd 里面.

<!--more -->

fork

先认识一些基础的数据结构.

struct hurd_sigstate
  {
    spin_lock_t critical_section_lock; /* Held if in critical section.  */

    spin_lock_t lock;		/* Locks most of the rest of the structure.  */

    thread_t thread; // 线程ID
    struct hurd_sigstate *next; /* Linked-list of thread sigstates.  */

		// sigset_t 是一个由 unsigned long int 组成的数组 
		// 其最大长度为: 1024 / 8 * sizeof (unsigned long int)
    sigset_t blocked;		/* What signals are blocked.  */
    sigset_t pending;		/* Pending signals, possibly blocked.  */

    /* Signal handlers.  ACTIONS[0] is used to mark the threads with POSIX
       semantics: if sa_handler is SIG_IGN instead of SIG_DFL, this thread
       will receive global signals and use the process-wide action vector
       instead of this one.  */
    struct sigaction actions[_NSIG];

    stack_t sigaltstack;

    /* Chain of thread-local signal preemptors; see <hurd/sigpreempt.h>.
       Each element of this chain is in local stack storage, and the chain
       parallels the stack: the head of this chain is in the innermost
       stack frame, and each next element in an outermore frame.  */
    struct hurd_signal_preemptor *preemptors;

    /* For each signal that may be pending, the details to deliver it with.  */
    struct hurd_signal_detail pending_data[_NSIG];

    /* If `suspended' is set when this thread gets a signal,
       the signal thread sends an empty message to it.  */
    mach_port_t suspended;

    /* The following members are not locked.  They are used only by this
       thread, or by the signal thread with this thread suspended.  */

    volatile mach_port_t intr_port; /* Port interruptible RPC was sent on.  */

    /* If this is not null, the thread is in sigreturn awaiting delivery of
       pending signals.  This context (the machine-dependent portions only)
       will be passed to sigreturn after running the handler for a pending
       signal, instead of examining the thread state.  */
    struct sigcontext *context;

    /* This is the head of the thread's list of active resources; see
       <hurd/userlink.h> for details.  This member is only used by the
       thread itself, and always inside a critical section.  */
    struct hurd_userlink *active_resources;

    /* These are locked normally.  */
    int cancel;			/* Flag set by hurd_thread_cancel.  */
    void (*cancel_hook) (void);	/* Called on cancellation.  */
  };

/* Linked list of states of all threads whose state has been asked for.  */

emm... 看不懂... Orz

姑且先理解为: 信号相关配置.

这代码量. 不愧是 fork 呢...

我看不懂! 差了太多必要的东西了. 我裂开了... 不! cas 你行的.

cas 你可以的!

cas 你可以的!

那么, 一点一点来分析. (因为神秘缘故上面代码没有语法高亮(已经删除了). 所以这也拆开也比较好!)

/* 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; // jmp_buf 是个后续会用到的值
	pid_t pid; // 很显然就是子进程 ID 了
  size_t i;
  error_t err;
  struct hurd_sigstate *volatile ss;

	
// 	2013-10-04  Samuel Thibault  <samuel.thibault@ens-lyon.org>
// 	
// 		* sysdeps/mach/hurd/fork.c (_hurd_atfork_prepare_hook)
// 		(_hurd_atfork_child_hook, _hurd_atfork_parent_hook): New hooks.
// 		(__fork): Call _hurd_atfork_prepare_hook hooks before all locking, call
// 		_hurd_atfork_parent_hook or _hurd_atfork_child_hook after all unlocking.
	// 关于这个, 我找到了上述日志. (可惜是 13 年的, 不然想去打扰一下作者)
	// 看起来是在 fork 时的准备工作.
	RUN_HOOK (_hurd_atfork_prepare_hook, ())

  ss = _hurd_self_sigstate ();
  __spin_lock (&ss->critical_section_lock);

#undef	LOSE
#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
// 关于 _hurd_atfork_prepare_hook :
struct atfork
{
  void (*prepare) (void);
  void (*parent) (void);
  void (*child) (void);
  void *dso_handle;
  struct atfork *prev;
  struct atfork *next;
};

/* TODO: better locking */
__libc_lock_define_initialized (static, atfork_lock);
static struct atfork *fork_handlers, *fork_last_handler;

// 这个函数的意图挺好理解的, 循环遍历. 执行准备工作.
// 不过问题是, 这些工作是从哪里设置的 = =
// fork_handles 和 fork_last_handler 看起来应该是有一段'距离'的, 这又是哪里设置的呢 = =
static void
atfork_pthread_prepare (void)
{
  struct atfork *handlers, *last_handler;

  __libc_lock_lock (atfork_lock);
  handlers = fork_handlers;
  last_handler = fork_last_handler;
  __libc_lock_unlock (atfork_lock);

  if (last_handler == NULL)
    return;

  while (1)
    {
      if (last_handler->prepare != NULL)
	last_handler->prepare ();
      if (last_handler == handlers)
	break;
      last_handler = last_handler->prev;
    }
}
text_set_element (_hurd_atfork_prepare_hook, atfork_pthread_prepare);
// 关于setjmp: [link](<https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/setjmp.htm>) 
// 简而言之, 它会保存当前环境, 而之后在 longjmp 则会返回到当前环境. 
// 而此时, setjmp 返回非 0. 类似于一个开关.
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;
      mach_msg_type_number_t statecount;
      mach_port_t *portnames = NULL;
      mach_msg_type_number_t nportnames = 0;
      mach_port_type_t *porttypes = NULL;
      mach_msg_type_number_t nporttypes = 0;
      thread_t *threads = NULL;
      mach_msg_type_number_t nthreads = 0;
      int ports_locked = 0, stopped = 0;
// 这里是我极其疑惑的一点. 这不是函数定义么?
// 函数中函数定义? 不可以这么做啊... Orz
		void resume_threads (void)
	{
	  if (! stopped)
	    return;

	  assert (threads);

	  for (i = 0; i < nthreads; ++i)
	    if (threads[i] != ss->thread)
	      __thread_resume (threads[i]);
	  stopped = 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.  */
      {
	void *const *p;
	for (p = symbol_set_first_element (_hurd_fork_locks);
	     ! symbol_set_end_p (_hurd_fork_locks, p);
	     ++p)
	  __mutex_lock (*p);
      }
      __mutex_lock (&_hurd_siglock);