快速锁和递归锁,线上遇到锁问题分析

快速锁和递归锁,线上遇到锁问题分析


前言

最近线上出现CPU飚到 99%~101%, 这个情况就可以肯定判断是项目中代码进入到了死循环中了。 当时调试线上进程看到[__gthread_mutex_lock]这个函数上了。

一、锁介绍

1、锁的实现有两种实现方式

  1. 硬件实现
  2. 软件实现

① 硬件实现

② 软件实现

就是写一个标记位,当前标记位有人使用的时候就wait使要使用进入wait状态, 当前使用的状态是lock状态。

③ 实现使当前进程进入等待状态就是死锁

// 编译指令
// gcc -o die_lock die_lock.c -lpthread -g -Wall
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#define UNUSED(OBJECT) (void)OBJECT
/// 死锁
void lock()
{
        int ret = 0;
        pthread_mutex_t mutex;
        ret = pthread_mutex_init(&mutex, NULL);
        assert(ret == 0);
        ret = pthread_mutex_lock(&mutex);
        assert(ret == 0);
        // 互斥锁 [__lll_lock_wait] 直到不用了 才会释放该锁  在使用得时候想加锁时就需要等待中了  [__lll_lock_wait]
        ret = pthread_mutex_lock(&mutex);
        assert(ret == 0);
		ret = pthread_mutex_destroy(&mutex);
        assert(ret == 0);
}

//正常使用不会死锁的
void lock_run()
{
int ret = 0;
        pthread_mutex_t mutex;
        ret = pthread_mutex_init(&mutex, NULL);
        assert(ret == 0);
        ret = pthread_mutex_lock(&mutex);
        assert(ret == 0);
        ret = pthread_mutex_unlock(&mutex);
        assert(ret == 0);
        ret = pthread_mutex_lock(&mutex);
        assert(ret == 0);
        ret = pthread_mutex_unlock(&mutex);
        assert(ret == 0);
        ret = pthread_mutex_destroy(&mutex);
        assert(ret == 0);
}


int main(int argc, char *argv[])
{
	lock();
	return 0;
}

在这里插入图片描述

④ 锁具体的使用

 /***********************************************************************************************
					created: 2020-05-20 
					author: chensong
					purpose: mutex 死锁问题 锁大致分为三种
								1. fast 快速  又名悲观锁 -- 互斥锁
								2. recursive 递归 又名乐观锁
								3. error check 错误检查
************************************************************************************************/
#include <stdio.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <assert.h>

#define UNUSED(OBJECT) (void)OBJECT
// mutex -> 1. 'fast' 快速锁 default [PTHREAD_MUTEX_FAST_NP]
// 2. 'recursive' 递归锁 [PTHREAD_MUTEX_RECURSIVE_NP]
// 3. error check [PTHREAD_MUTEX_ERRORCHECK_NP] pthread_mutex_t mutex;
// 1 . 悲观锁
pthread_mutex_t mutex; 
void fast_mutex() 
{
	printf("[%s][%d]\n", __FUNCTION__, __LINE__); 
	pthread_mutexattr_t attr;
	int ret = 0;
	ret = pthread_mutexattr_init(&attr);
	assert(ret == 0);
	UNUSED(ret);
	int kind = 0;//mutex_attr
	ret = pthread_mutexattr_gettype(&attr, &kind); 
	assert(ret == 0);
	printf("[mutexattr_gettype kind = %d]\n", kind); 
	ret = pthread_mutex_init(&mutex, &attr);
	assert(ret == 0);
	ret = pthread_mutexattr_destroy(&attr); 
	assert(ret == 0);
	printf("[mutex init ok]\n"); //usleep(1000*1000);
	printf("timeout we will start dead lock\n");
	ret = pthread_mutex_lock(&mutex);
	assert(ret == 0);
	printf("[1mutex lock ok]\n");
	ret = pthread_mutex_lock(&mutex);
	assert(ret == 0);
	printf("[2mutex lock ok]\n");
}
// 2. 乐观锁
void recursive_mutex()
{ //
	pthread_mutexattr_t attr;
	int ret = 0;
	ret = pthread_mutexattr_init(&attr); 
	assert(ret == 0);
	UNUSED(ret);
	int kind = 0;//mutex_attr
	ret = pthread_mutexattr_gettype(&attr, &kind); 
	assert(ret == 0);
	printf("[mutexattr_gettype kind = %d]\n", kind);
	kind = PTHREAD_MUTEX_RECURSIVE_NP;
	ret = pthread_mutexattr_settype(&attr, kind); 
	assert(ret == 0);
	printf("[mutexattr_settype ok]");
	ret = pthread_mutexattr_gettype(&attr, &kind); 
	assert(ret == 0);
	printf("[mutexattr_gettype new kind = %d]\n", kind); 
	ret = pthread_mutex_init(&mutex, &attr);
	assert(ret == 0);
	ret = pthread_mutexattr_destroy(&attr);
	printf("[mutex init ok]\n");
	printf("timeout we will start dead lock\n"); 
	ret = pthread_mutex_lock(&mutex); 
	assert(ret == 0);
	printf("[1mutex lock ok]\n");
	ret = pthread_mutex_lock(&mutex); 
	assert(ret == 0);
	printf("[2mutex lock ok]\n");
	while (1)
	{
		usleep(1000*1000); 
	}
}
void error_mutex()
{
	// pthread_mutex_t mutex;
	pthread_mutexattr_t attr;
	int ret = 0;
	ret = pthread_mutexattr_init(&attr);
	assert(ret == 0);
	//assert(ret != 0);
	UNUSED(ret);
	int kind = 0;//mutex_attr
	ret = pthread_mutexattr_gettype(&attr, &kind); 
	assert(ret == 0);
	printf("[mutexattr_gettype kind = %d]\n", kind);
	kind = PTHREAD_MUTEX_ERRORCHECK_NP; 
	ret = pthread_mutexattr_settype(&attr, kind); 
	assert(ret == 0);
	printf("[mutexattr_settype ok]");
	ret = pthread_mutexattr_gettype(&attr, &kind); 
	assert(ret == 0);
	printf("[mutexattr_gettype new kind = %d]\n", kind); 
	ret = pthread_mutex_init(&mutex, &attr);
	assert(ret == 0);
	//assert(ret != 0);
	ret = pthread_mutexattr_destroy(&attr);
	assert(ret == 0);
	//assert(ret != 0);

	 printf("[mutex init ok]\n");
	printf("timeout we will start dead lock\n"); 
	ret = pthread_mutex_lock(&mutex); 
	assert(ret == 0);
//	assert(ret != 0);
	printf("[1mutex lock ok]\n"); //usleep(1000*1000);
	ret = pthread_mutex_lock(&mutex); 
	assert(ret == 0);
	printf("[2mutex lock ok]\n");
}
void *work_thread(void *arg) 
{
//		 fast_mutex();
		recursive_mutex(); 
		//error_mutex(); 
		return NULL;

}
void *alive_thread(void *arg) 
{
	int ret = 0;
	usleep(1000*1000);
	printf("[%s][%d]\n", __FUNCTION__, __LINE__); 
	ret = pthread_mutex_lock(&mutex);
	assert(ret == 0);
	//assert(ret != 0);
	UNUSED(ret);
	printf("[use mutex ok !!!]\n");
	while (1)
	{
		usleep(1000*1000); 
	}
}
int main(int argc, char *argv[]) 
{
	pthread_t dead_pid; 
	pthread_create(&dead_pid, 0, work_thread, 0); 
	pthread_t alive_pid;
	pthread_create(&alive_pid, 0, alive_thread, 0); 
	void *ret = NULL; //assert(pthread_join(dead_pid, &ret) != 0); 
	pthread_join(dead_pid, &ret) ;
	void *ret2 = NULL; //assert(pthread_join(alive_pid, &ret2) != 0); 
	pthread_join(alive_pid, &ret2);
	UNUSED(ret);
	UNUSED(ret2);
	return 0; 
}


project(cmutex)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
MESSAGE(STATUS "#########################")

ADD_DEFINITIONS( -g -Wall )

file(GLOB ALL_C_SRCS
./*.c
)

add_executable(${PROJECT_NAME} ${ALL_C_SRCS} )


set(ALL_LIB  pthread
	)


target_link_libraries(${PROJECT_NAME} ${ALL_LIB})


  • fast

在这里插入图片描述

  • recursive_mutex

在这里插入图片描述

二、线上的问题分析

问题效果图
在这里插入图片描述

上面是实际项目的问题

中途我把互斥锁给排除了, 这个问题只有递归锁才会触发(根据堆栈信息给排除了[ __gthread_active_p, __gthread_mutex_unlock]两个函数, 线程的状态不是等待状态(wait))。

1、中途我查看gun5.3的源码看一下递归锁怎么实现的

recursive_mutex的源码实现


// 设置递归锁
#if !defined( PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) \
 || defined(_GTHREAD_USE_RECURSIVE_MUTEX_INIT_FUNC)
static inline int
__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
{
 if (__gthread_active_p ())
   {
     pthread_mutexattr_t __attr;
     int __r;

     __r = __gthrw_(pthread_mutexattr_init) (&__attr);
     if (!__r)
   __r = __gthrw_(pthread_mutexattr_settype) (&__attr,
   					   PTHREAD_MUTEX_RECURSIVE); 
     if (!__r)
   __r = __gthrw_(pthread_mutex_init) (__mutex, &__attr);
     if (!__r)
   __r = __gthrw_(pthread_mutexattr_destroy) (&__attr);
     return __r;
   }
 return 0;
}
#endif

static inline int
__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
{
 return __gthread_mutex_lock (__mutex);
}

static inline int
__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
{
 return __gthread_mutex_trylock (__mutex);
}

#if _GTHREAD_USE_MUTEX_TIMEDLOCK
static inline int
__gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *__mutex,
   			     const __gthread_time_t *__abs_timeout)
{
 return __gthread_mutex_timedlock (__mutex, __abs_timeout);
}
#endif

static inline int
__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
{
 return __gthread_mutex_unlock (__mutex);
}

static inline int
__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
{
 return __gthread_mutex_destroy (__mutex);
}


  // Common base class for std::recursive_mutex and std::recursive_timed_mutex
 class __recursive_mutex_base
 {
 protected:
   typedef __gthread_recursive_mutex_t		__native_type;

   __recursive_mutex_base(const __recursive_mutex_base&) = delete;
   __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;

#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
   __native_type  _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;

   __recursive_mutex_base() = default;
#else
   __native_type  _M_mutex;

   __recursive_mutex_base()
   {
     // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
     __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);//__gthread_recursive_mutex_init_function
   }

   ~__recursive_mutex_base()
   { __gthread_recursive_mutex_destroy(&_M_mutex); }
#endif
 };


 /// recursive_mutex
 class recursive_mutex : private __recursive_mutex_base
 {
 public:
   typedef __native_type* 			native_handle_type;

   recursive_mutex() = default;
   ~recursive_mutex() = default;

   recursive_mutex(const recursive_mutex&) = delete;
   recursive_mutex& operator=(const recursive_mutex&) = delete;

   void
   lock()
   {
     int __e = __gthread_recursive_mutex_lock(&_M_mutex);

     // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
     if (__e)
   __throw_system_error(__e);
   }

   bool
   try_lock() noexcept
   {
     // XXX EINVAL, EAGAIN, EBUSY
     return !__gthread_recursive_mutex_trylock(&_M_mutex);
   }

   void
   unlock()
   {
     // XXX EINVAL, EAGAIN, EBUSY
     __gthread_recursive_mutex_unlock(&_M_mutex);
   }

   native_handle_type
   native_handle()
   { return &_M_mutex; }
 };

2、问题重现

下面我使用最简单的c语言来实现上面一样的问题 CPU 99%~101%

// 编译指令
// gcc -o die_lock die_lock.c -lpthread -g -Wall
 #include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>


#define UNUSED(OBJECT) (void)OBJECT


pthread_mutex_t recursive_mutex;

void recursive_mutex_three_lock(pthread_mutex_t * recursive_mutex)
{
        // 解   
        pthread_mutex_t mutex = *recursive_mutex;

        pthread_mutexattr_t attr;
        int ret = 0;
        ret = pthread_mutexattr_init(&attr);
        assert(ret == 0);
        UNUSED(ret);
        int kind = 0;//mutex_attr
        ret = pthread_mutexattr_gettype(&attr, &kind);
        assert(ret == 0);
        kind = PTHREAD_MUTEX_RECURSIVE_NP;
        ret = pthread_mutexattr_settype(&attr, kind);
        assert(ret == 0);
        ret = pthread_mutexattr_gettype(&attr, &kind);
        assert(ret == 0);
        ret = pthread_mutex_init(&mutex, &attr);
        assert(ret == 0);
        ret = pthread_mutexattr_destroy(&attr);
        assert(ret == 0);
        ret = pthread_mutex_lock(&mutex);
        assert(ret == 0);
        ret = pthread_mutex_unlock(&mutex);
        assert(ret == 0);
        

}
int get_three_lock()
{
        recursive_mutex_three_lock(&recursive_mutex);
        return 1;

}
void show()
{
        while (1)
        {
        //不停调用该函数 使用会在底层锁上停留的时间比较长 --->[__GI___pthread_mutex_lock]
                int w = get_three_lock();
                UNUSED(w);
        }
}
int main()
{

       	show();

        return 0;
}

在这里插入图片描述

看堆栈信息是__GI__thread_mutex_init函数上的但是一直调用recursive_mutex_three_lock函数的在__GI__thread_mutex_init上停留的时间比较长

CPU使用情况
在这里插入图片描述

三、GDB的命令

  1. 查看线程:info threads
  2. 进入线程:thread num
  3. gdb 调试进入函数体: ‘s’
  4. 查看堆栈信息: bt

总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值