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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

pthread_mutex_lock和sem_wait对信号唤醒的处理  

2011-02-27 00:20:52|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、函数pthread_mutex_lock的执行

Posix规定,pthread_mutex_lock是不能被信号唤醒的,但是可以执行信号。也就是说,当线程在执行pthread_mutex_lock函数的时候,接受到的信号可以被执行,但是信号执行完之后需要继续等待,而不能从pthread_mutex_lock函数返回。

然后我们看一下这个函数的实现:

pthread_mutex_lock

    /* Recursive mutex.  */
    case PTHREAD_MUTEX_RECURSIVE_NP:
      /* Check whether we already hold the mutex.  */
      if (mutex->__data.__owner == id)
 {
   /* Just bump the counter.  */
   if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
     /* Overflow of the counter.  */
     return EAGAIN;

   ++mutex->__data.__count;

   return 0;
 }

      /* We have to get the mutex.  */
      LLL_MUTEX_LOCK (mutex); 从这个函数进入真正的底层互斥锁等待函数。

因为个人对386比较熟悉,所以以这部分代码为样板进行分析

#define lll_lock(futex, private) \
  (void)              \
    ({ int ignore1, ignore2;            \
       if (__builtin_constant_p (private) && (private) == LLL_PRIVATE)       \
  __asm __volatile (__lll_lock_asm_start          \
      "jnz _L_lock_%=\n\t"          \
      ".subsection 1\n\t"          \
      ".type _L_lock_%=,@function\n"        \
      "_L_lock_%=:\n"          \
      "1:\tleal %2, %%ecx\n"         \
      "2:\tcall __lll_lock_wait_private\n"        \
      "3:\tjmp 18f\n"          \
      "4:\t.size _L_lock_%=, 4b-1b\n\t"        \
      ".previous\n"          \
      LLL_STUB_UNWIND_INFO_3         \
      "18:"           \
      : "=a" (ignore1), "=c" (ignore2), "=m" (futex)     \
      : "0" (0), "1" (1), "m" (futex),        \
        "i" (MULTIPLE_THREADS_OFFSET)        \
      : "memory");           \
       else              \
  {              \
    int ignore3;             \
    __asm __volatile (__lll_lock_asm_start         \
        "jnz _L_lock_%=\n\t"         \
        ".subsection 1\n\t"         \
        ".type _L_lock_%=,@function\n"        \
        "_L_lock_%=:\n"          \
        "1:\tleal %2, %%edx\n"         \
        "0:\tmovl %8, %%ecx\n"         \
        "2:\tcall __lll_lock_wait\n"        \
        "3:\tjmp 18f\n"          \
        "4:\t.size _L_lock_%=, 4b-1b\n\t"        \
        ".previous\n"          \
        LLL_STUB_UNWIND_INFO_4         \
        "18:"           \
        : "=a" (ignore1), "=c" (ignore2),        \
          "=m" (futex), "=&d" (ignore3)         \
        : "1" (1), "m" (futex),         \
          "i" (MULTIPLE_THREADS_OFFSET), "0" (0),       \
          "g" (private)          \
        : "memory");          \
  }              \
    })

void
__lll_lock_wait_private (int *futex)
{
  do
    {
      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
      if (oldval != 0)
 lll_futex_wait (futex, 2, LLL_PRIVATE); 可以看到,这里根本没有判断lll_futex_wait的返回值,不论内核返回的是-EWOULDBLOCK或者是-EINTR,对这个循环的终止都没有意义。而这个循环终止的唯一条件就是futex的值变为0,而这个0值也就是由pthread_mutex_unlock来完成,所以信号是不能把一个pthread_mutex唤醒的
    }
  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0); 看一下这个循环终止的条件,这个底层的lll_futex_wait是sem_wait和pthread_mutex_lock所共享的一个底层接口,而是否真正从函数返回就看这个循环条件终止的而不同
}

#define lll_futex_wait(futex, val, private) \
  lll_futex_timed_wait (futex, val, NULL, private)


#define lll_futex_timed_wait(futex, val, timeout, private) \
  ({               \
    int __status;             \
    register __typeof (val) _val asm ("edx") = (val);         \
    __asm __volatile (LLL_EBX_LOAD           \
        LLL_ENTER_KERNEL           \
        LLL_EBX_LOAD           \
        : "=a" (__status)           \
        : "0" (SYS_futex), LLL_EBX_REG (futex), "S" (timeout),  \
   "c" (__lll_private_flag (FUTEX_WAIT, private)),       \
   "d" (_val), "i" (offsetof (tcbhead_t, sysinfo))       \
        : "memory");           \
    __status;              \
  })

二、sem_wait的执行

 do
    {
      if (atomic_decrement_if_positive (futex) > 0)
 return 0;

      /* Enable asynchronous cancellation.  Required by the standard.  */
      int oldtype = __pthread_enable_asynccancel ();

      /* Always assume the semaphore is shared.  */
      err = lll_futex_wait (futex, 0, LLL_SHARED); 这里循环将会以来系统的返回值。可以看到,系统返回-EINTR的时候会从sem_wait返回,这一点是POSIX标准规定的,不存在技术上实现的难点。而这里的EWOULDBLOCK从内核可以看到,就是我们进行futex_wait的时候,会传入我们认为或者期望的futex的值,如果这个值在进入内核之前被其它任务修改了,那么内核就返回一个EWOULDBLOCK给用户态,这个是多线程竞争的关系

      /* Disable asynchronous cancellation.  */
      __pthread_disable_asynccancel (oldtype);
    }
  while (err == 0 || err == -EWOULDBLOCK);

三、Robust 互斥锁的实现

int
__lll_robust_lock_wait (int *futex, int private)
{
  int oldval = *futex;
  int tid = THREAD_GETMEM (THREAD_SELF, tid);

  /* If the futex changed meanwhile try locking again.  */
  if (oldval == 0)
    goto try;

  do
    {
      if (__builtin_expect (oldval & FUTEX_OWNER_DIED, 0)) 如果这个锁的地址被设置为FUTEX_OWNER_DEAD标志,那么此时同样需要返回,因为从这里返回才能执行被唤醒操作。这是robust mutex区别于普通mutex的一耳光重要特性
 return oldval;

      int newval = oldval | FUTEX_WAITERS;
      if (oldval != newval
   && atomic_compare_and_exchange_bool_acq (futex, newval, oldval))
 continue;

      lll_futex_wait (futex, newval, private);

    try:
      ;
    }
  while ((oldval = atomic_compare_and_exchange_val_acq (futex,
       tid | FUTEX_WAITERS,
       0)) != 0);
  return 0;
}
看一下内核对这个属性标志的设置

 


/*
 * Process a futex-list entry, check whether it's owned by the
 * dying task, and do notification if so:
 */
int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
{
 u32 uval, nval, mval;

retry:
 if (get_user(uval, uaddr))
  return -1;

 if ((uval & FUTEX_TID_MASK) == task_pid_vnr(curr)) {
  /*
   * Ok, this dying thread is truly holding a futex
   * of interest. Set the OWNER_DIED bit atomically
   * via cmpxchg, and if the value had FUTEX_WAITERS
   * set, wake up a waiter (if any). (We have to do a
   * futex_wake() even if OWNER_DIED is already set -
   * to handle the rare but possible case of recursive
   * thread-death.) The rest of the cleanup is done in
   * userspace.
   */
  mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; 这里将这个锁的OWNER_DEAD标志设置到futex中,从而导致robust互斥锁从用户态的互斥锁中返回
  nval = futex_atomic_cmpxchg_inatomic(uaddr, uval, mval);

  if (nval == -EFAULT)
   return -1;

  if (nval != uval)
   goto retry;

  /*
   * Wake robust non-PI futexes here. The wakeup of
   * PI futexes happens in exit_pi_state():
   */
  if (!pi && (uval & FUTEX_WAITERS))
   futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
 }
 return 0;
}

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

历史上的今天

评论

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

页脚

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