注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

pthread_join和pthread_detach  

2011-08-11 21:49:10|  分类: Glibc分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、GLIBC中pthread结构中相关定义

在该结构中定义了两个pid_t变量,和一个C库的pthread变量


  /* Thread ID - which is also a 'is this thread descriptor (and
     therefore stack) used' flag.  */
  pid_t tid; 这里的tid表示这个线程本身在内核中的LWD ID,这个是一般是和这个pthread一一对应的

  /* Process ID - thread group ID in kernel speak.  */
  pid_t pid;线程组ID

  /* If the thread waits to join another one the ID of the latter is
     stored here.

     In case a thread is detached this field contains a pointer of the
     TCB if the thread itself.  This is something which cannot happen
     in normal operation.  */
  struct pthread *joinid;这个事实上是一个三态变量:执行过join、执行过detach,两者均没有

二、pthread_join和pthread_detach的实现

1、状态判断

从上面的描述可以看到,一个线程的detach和join是不能同时存在的,所以在两个操作中都是进行了判断。其中如果joinid为空,表示join和detach均未进行;如果joinid等于自己,说明已经执行过detach函数;如果不是前两者,那么说明已经有线程对该线程执行过了join操作。

2、pthread_detach实现

int
pthread_detach (th)
     pthread_t th;
{
  struct pthread *pd = (struct pthread *) th;

  /* Make sure the descriptor is valid.  */
  if (INVALID_NOT_TERMINATED_TD_P (pd))
    /* Not a valid thread handle.  */
    return ESRCH;

  int result = 0;

  /* Mark the thread as detached.  */
  if (atomic_compare_and_exchange_bool_acq (&pd->joinid, pd, NULL))只有在这个joinid为零的时候才能进行detach操作。如果非零,这里的atomi操作返回值非零
    {
      /* There are two possibilities here.  First, the thread might
  already be detached.  In this case we return EINVAL.
  Otherwise there might already be a waiter.  The standard does
  not mention what happens in this case.  */
      if (IS_DETACHED (pd))
 result = EINVAL;如果已经执行过detach,这里的操作就是多余的,但是如果没有被detach,说明已经有线程提前执行了pthread_join,此时没有做任何操作,返回值使用默认值0.
    }
  else
    /* Check whether the thread terminated meanwhile.  In this case we
       will just free the TCB.  */
    if ((pd->cancelhandling & EXITING_BITMASK) != 0)
      /* Note that the code in __free_tcb makes sure each thread
  control block is freed only once.  */
      __free_tcb (pd);

  return result;
}
3、pthread_join的实现

这个函数的意义就是调用线程将会在这里阻塞,直到join的线程退出。

int
pthread_join (threadid, thread_return)
     pthread_t threadid;
     void **thread_return;
{
  struct pthread *pd = (struct pthread *) threadid;

  /* Make sure the descriptor is valid.  */
  if (INVALID_NOT_TERMINATED_TD_P (pd))
    /* Not a valid thread handle.  */
    return ESRCH;

  /* Is the thread joinable?.  */
  if (IS_DETACHED (pd)) 这里同样进行是否进行过detach操作,如果已经执行过detach,那么此时直接返回
    /* We cannot wait for the thread.  */
    return EINVAL;

  struct pthread *self = THREAD_SELF;
  int result = 0;

  /* During the wait we change to asynchronous cancellation.  If we
     are canceled the thread we are waiting for must be marked as
     un-wait-ed for again.  */
  pthread_cleanup_push (cleanup, &pd->joinid);

  /* Switch to asynchronous cancellation.  */
  int oldtype = CANCEL_ASYNC ();

  if ((pd == self
       || (self->joinid == pd
    && (pd->cancelhandling
        & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
    | TERMINATED_BITMASK)) == 0))
      && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))
    /* This is a deadlock situation.  The threads are waiting for each
       other to finish.  Note that this is a "may" error.  To be 100%
       sure we catch this error we would have to lock the data
       structures but it is not necessary.  In the unlikely case that
       two threads are really caught in this situation they will
       deadlock.  It is the programmer's problem to figure this
       out.  */
    result = EDEADLK;
  /* Wait for the thread to finish.  If it is already locked something
     is wrong.  There can only be one waiter.  */
  else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid,
           self,
           NULL), 0))这里一般是不会出错的,所以返回值为零,从而会继续执行接下来的else中的lll_wait操作
    /* There is already somebody waiting for the thread.  */
    result = EINVAL;
  else
    /* Wait for the child.  */
    lll_wait_tid (pd->tid);


  /* Restore cancellation mode.  */
  CANCEL_RESET (oldtype);

  /* Remove the handler.  */
  pthread_cleanup_pop (0);


  if (__builtin_expect (result == 0, 1))
    {
      /* We mark the thread as terminated and as joined.  */
      pd->tid = -1;

      /* Store the return value if the caller is interested.  */
      if (thread_return != NULL)
 *thread_return = pd->result;


      /* Free the TCB.  */
      __free_tcb (pd);一旦执行到上面的lll_wait_tid,那么这个操作时不可避免的。注意,这里传入的是被等待线程的pthread结构中的tid
    }

  return result;
}
4、何时唤醒

在线程创建的时候,clone创建的flags中为

create_thread

  int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
       | CLONE_SETTLS | CLONE_PARENT_SETTID
       | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
#if __ASSUME_NO_CLONE_DETACHED == 0
       | CLONE_DETACHED
#endif
       | 0);

 

     CLONE_PARENT_SETTID
 The kernels writes the thread ID of the newly created thread
 into the location pointed to by the fifth parameters to CLONE.

 Note that it would be semantically equivalent to use
 CLONE_CHILD_SETTID but it is be more expensive in the kernel.

     CLONE_CHILD_CLEARTID
 The kernels clears the thread ID of a thread that has called
 sys_exit() in the location pointed to by the seventh parameter
 to CLONE.

 

  if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
    pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)

所以,新创建线程的tid放入上面的pd->tid结构中,并且通过CLONE_CHILD_CLEARTID要求新创建的线程在退出的时候清空第七个参数指向的用户地址,并唤醒在这个地址上睡眠的线程

内核中一个线程退出时执行的操作为

void mm_release(struct task_struct *tsk, struct mm_struct *mm)
/*
  * If we're exiting normally, clear a user-space tid field if
  * requested.  We leave this alone when dying by signal, to leave
  * the value intact in a core dump, and to save the unnecessary
  * trouble otherwise.  Userland only wants this done for a sys_exit.
  */
 if (tsk->clear_child_tid
     && !(tsk->flags & PF_SIGNALED)
     && atomic_read(&mm->mm_users) > 1) {
  u32 __user * tidptr = tsk->clear_child_tid;
  tsk->clear_child_tid = NULL;

  /*
   * We don't check the error code - if userspace has
   * not set up a proper pointer then tough luck.
   */
  put_user(0, tidptr);
  sys_futex(tidptr, FUTEX_WAKE, 1, NULL, NULL, 0); 这里只唤醒一个等待线程,也就是说用户态只能有一个线程在等待这个线程
 }

  评论这张
 
阅读(1810)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017