关于localtime函数引入的一系列glibc死锁问题及解决方案

问题场景:

        线程1:localtime  -->  fork  -- 子进程1 -->  localtime

        线程2:localtime  -->  fork  -- 子进程2 -->  localtime

        线程x:   localtime  -->  fork  -- 子进程x -->  localtime

问题分析:

        同事使用上述场景时候,遇到了死锁问题,死锁位置在随机子进程的localtime函数内,通过对glibc源码分析发现,localtime内部存在全局锁,而fork出的子进程会复制父进程的全局变量(但不共享),那么就存在如下场景会出现死锁问题:

        线程1:localtime →  加锁 tzset_lock = 1 ----→  解锁 tzset_lock = 0

                                                  ↓ 调度                       ↑ 调度

        线程2:                           fork   ----------------→   wait

                                                  ↓

        子进程2:                     tzset_lock  = 1 (此时复制过来的全局锁是锁住的状态,且不和主进程共享,子进程2一旦调用localtime走到加锁的步骤,必然会死锁)

glibc源码如下,localtime实际调用的是__tz_convert函数:

/* This locks all the state variables in tzfile.c and this file.  */
__libc_lock_define_initialized (static, tzset_lock)

struct tm *
__tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
{
  long int leap_correction;
  int leap_extra_secs;

  if (timer == NULL)
    {
      __set_errno (EINVAL);
      return NULL;
    }

  __libc_lock_lock (tzset_lock);    //实际tzset_lock就是个全局的mutex锁

  /* Update internal database according to current TZ setting.
     POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
     This is a good idea since this allows at least a bit more parallelism.  */
  tzset_internal (tp == &_tmbuf && use_localtime, 1);

  if (__use_tzfile)
    __tzfile_compute (*timer, use_localtime, &leap_correction,
		      &leap_extra_secs, tp);
  else
    {
      if (! __offtime (timer, 0, tp))
	tp = NULL;
      else
	__tz_compute (*timer, tp, use_localtime);
      leap_correction = 0L;
      leap_extra_secs = 0;
    }

  if (tp)
    {
      if (! use_localtime)
	{
	  tp->tm_isdst = 0;
	  tp->tm_zone = "GMT";
	  tp->tm_gmtoff = 0L;
	}

      if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))
        tp->tm_sec += leap_extra_secs;
      else
	tp = NULL;
    }

  __libc_lock_unlock (tzset_lock);

  return tp;
}

问题延伸:

        最开始同事解决的方案是自定义了localtime,规避了上述问题,但又发现执行system时候也有概率产生死锁,原理实际和localtime类似,system也会使用全局锁。

        以此类推,glibc应该存在很多函数都有上述场景的死锁问题,建议大家在类似场景使用的时候了解清楚实现机制,避免死锁问题。

解决方案:

        对于上述场景,将fork改为vfork就可以解决glibc存在的一些列死锁问题,vfork的子进程与父进程之间共享全局变量,这样父进程解锁后可以同步给子进程,子进程就不会存在死锁问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值