gdbを使用してスレッドのデッドロック問題を位置決めする

15895 ワード

デッドロックコンセプト
デッドロックとは、簡単に言えば、デッドロックはA、Bの2つのスレッド、mtx_a,mtx_b 2つの反発量.スレッドAがmtx_を持っている場合aと同時にmtx_を競うb,ちょうどスレッドBがmtx_を持つb,mtx_を競うa,このとき,2つのスレッドが競合しようとするリソースがちょうど相手に占有され,リソースの解放を待っていたため,スレッドデッドロックの現象に陥った.
 
デッドロックの例
通常、コードを書く過程で発生しやすいデッドロックは主に以下のいくつかあります.それぞれの状況について簡単な例を書いて問題点を暴露します.もちろん、他の状況については、ここでは一つ一つ例を挙げて説明しません.
1.2つ以上の反発ロック、ロック順序の問題
/*
        ,        pushData,         popData。
           :pushData  mutex1,  mutex2,  popData  mutex2,  mutex1。
*/
class dataPool
{
public:
    dataPool()=default;
    void pushData(const std::string& data)
	{
	    std::unique_lock<:mutex> lck1(mutex1);
	    std::unique_lock<:mutex> lck2(mutex2);
	    dataPool_.push_back(data);
	}
	void popData(const std::string& data)
	{
	    std::unique_lock<:mutex> lck2(mutex2);
	    std::unique_lock<:mutex> lck1(mutex1);
	    dataPool_.pop_front(data);
	}
private:
    std::mutex mutex1;
    std::mutex mutex2;
    std::list<:string> dataPool_;
}

2.ネストロック
/*
              ,                  。
    pushData  mutex_  ,    findData    mutex_,    。
*/
class dataPool
{
public:
	dataPool()=default;
	void pushData(int code,const std::string& data)
	{
		std::unique_lock<:mutex> lck(mutex_);
		if(!findData(code))
		{
			dataMap_[code]=data;
		}
	}
	bool findData(int code)
	{
		bool bRet=false;
		std::unique_lock<:mutex> lck(mutex_);
		auto it=dataMap_.find(code);
		if(it!=dataMap_.end())
		{
			bRet=true;
		}
		return bRet;
	}
private:
	std::mutex mutex_;
	std::map dataMap_;
}

3.ロック解除忘れ
/*
              ,      c++11    ,                   ,
          。
*/
class dataPool
{
public:
	dataPool()=default;
	void pushData(const std::string& data)
	{
		mutex_.lock();
		dataPool_.push_back(data);
		mutex_.unlock();
	}
	void popData(const std::string& data)
	{
		mutex_.lock();
		dataPool_.pop_front(data);
                //mutex_.unlock();
	}
private:
	std::mutex mutex_;
	std::list<:string> dataPool_;
}

回避方法
1.両反発量は常に同じ順序で施錠する:反発量Bの前に常に反発量Bを施錠する.ネストされたロックを避ける:1つのスレッドで2回のロックを取得しないでください.ロックがある場合は後でunlcokするかstd::unique_を使用します.ロック包装
 
位置決め方法
もしうっかりデッドロックが発生したら、私たちは位置決めを学ばなければなりません.1つの方法は、ログを介して大まかなデッドロックポイントを特定することですが、この方法は適用されない場合があります.ここでは,gdbデバッグを用いた位置決めデッドロックの問題について主に紹介する.
次のコードにメインスレッドを加えて3つのスレッドがあり、データ領域をpushとpop操作し、デッドロックの発生条件を構築してデッドロックの問題を再現します.
(gdbがスタック情報を表示するときにコード行番号を表示しないという問題がある場合は、コンパイルプロセスに-gオプションを追加します)
 
#include 
#include 
#include 
#include 
#include 
#include 
#define POOL_MAX_LEN (100)
#define POOL_DATA_MAX_SIZE (1*1024*1024)
using sourceData = struct sourceData_
{
	char* data;
	int dataLen;
};
struct delSourceData
{
	void operator()(sourceData* delData)
	{
		if (delData->data)
		{
			delete delData->data;
			delData->data = nullptr;
		}
		if (delData)
		{
			delete delData;
			delData = nullptr;
		}
	}
};
class DataPool
{
public:
	DataPool() :dataPool(100),
		poolBegin_(0),
		poolEnd_(0),
		poolEmpty_(true),
		poolFull_(false)
	{
		for (int idx = 0; idx < POOL_MAX_LEN;++idx)
		{
			std::shared_ptr tmpData(new sourceData, delSourceData());
			tmpData->data = new char[POOL_DATA_MAX_SIZE];
			dataPool[idx] = tmpData;
		}
	}
	~DataPool() = default;
	void pushToPool(std::shared_ptr inputData)
	{
		std::unique_lock<:mutex> lckOne(mutexOne_);
		std::unique_lock<:mutex> lckTwo(mutexTwo_);
		if (!inputData->data&&!poolFull_)
		{
			dataPool[poolEnd_]->dataLen = inputData->dataLen;
			int copyLen = inputData->dataLen>POOL_DATA_MAX_SIZE ? POOL_DATA_MAX_SIZE :
				inputData->dataLen;
			memcpy(dataPool[poolEnd_]->data, inputData->data, copyLen);
			poolEnd_ = (poolEnd_ + 1) % POOL_MAX_LEN;
			poolEmpty_ = false;
			poolFull_ = poolEmpty_ == poolFull_ ? true : false;
		}
	}
	void popFromPool(std::shared_ptr outData)
	{
		std::unique_lock<:mutex> lckTwo(mutexTwo_);
		std::unique_lock<:mutex> lckOne(mutexOne_);
		if (poolEmpty_)
		{
			outData->dataLen = dataPool[poolBegin_]->dataLen;
			memcpy(outData->data, dataPool[poolBegin_]->data, outData->dataLen);
			poolBegin_ = (poolBegin_ + 1) % POOL_MAX_LEN;
			poolFull_ = false;
			poolEmpty_ = poolEmpty_ == poolFull_ ? true : false;
		}
	}
	bool poolFull()
	{
		return poolFull_;
	}
	bool poolEmpty()
	{
		return poolEmpty_;
	}
private:
	std::vector<:shared_ptr> > dataPool;
	std::mutex mutexOne_;
	std::mutex mutexTwo_;
	int poolBegin_;
	int poolEnd_;
	bool poolEmpty_;
	bool poolFull_;
};
class FetchData
{
public:
	FetchData() :pool_(std::make_shared())
	{

	}
	~FetchData() = default;
	void pushData()
	{
		while (true)
		{
			std::shared_ptr inputData(new sourceData, delSourceData());
			inputData->data = new char[POOL_DATA_MAX_SIZE];
			pool_->pushToPool(inputData);
		}
	}
	void popData()
	{
		while (true)
		{
			std::shared_ptr outData(new sourceData, delSourceData());
			outData->data = new char[POOL_DATA_MAX_SIZE];
			pool_->popFromPool(outData);
			std::cout << "easy to occur deadlock" << std::endl;
		}
	}
private:
	std::shared_ptr pool_;
};
int main()
{
	std::shared_ptr fetchData = std::make_shared();
	std::thread tOne(&FetchData::pushData, fetchData);
	std::thread tTwo(&FetchData::popData, fetchData);
	tOne.join();
	tTwo.join();
	std::cout << "over" << std::endl;

	return 0;
}
1.      ,       core dump  
   ulimit -c unlimited

2.     
   [root@ia10k gdb]# ./a.out 
   only for a test
   only for a test

3.     id,  kill    core dump
   [root@ia10k gdb]# pidof a.out 
   21458
   [root@ia10k gdb]# kill -11 21458
   [root@ia10k gdb]# 
   #            core dump
   [root@ia10k gdb]# ./a.out 
   only for a test
   only for a test
   Segmentation fault (core dumped)
   [root@ia10k gdb]# ls
   a.out  core.18734  deadlock.cpp
   [root@ia10k gdb]#  

4. gdb  core  
   [root@ia10k gdb]# gdb a.out core.18734

5.            
(gdb) thread apply all bt

Thread 3 (Thread 0x7fdc50a40700 (LWP 18735)):
#0  0x00007fdc5799542d in __lll_lock_wait () from /usr/lib64/libpthread.so.0
#1  0x00007fdc57990dcb in _L_lock_812 () from /usr/lib64/libpthread.so.0
#2  0x00007fdc57990c98 in pthread_mutex_lock () from /usr/lib64/libpthread.so.0
#3  0x00000000004010dc in __gthread_mutex_lock (__mutex=0xeab098) at /usr/include/c++/4.8.2/x86_64-redhat-linux/bits/gthr-default.h:748
#4  0x000000000040141e in std::mutex::lock (this=0xeab098) at /usr/include/c++/4.8.2/mutex:134
#5  0x0000000000402575 in std::unique_lock<:mutex>::lock (this=0x7fdc50a3fd80) at /usr/include/c++/4.8.2/mutex:511
#6  0x0000000000401f99 in std::unique_lock<:mutex>::unique_lock (this=0x7fdc50a3fd80, __m=...) at /usr/include/c++/4.8.2/mutex:443
#7  0x000000000040183f in DataPool::pushToPool (this=0xeab058, inputData=std::shared_ptr (count 2, weak 0) 0x7fdc480008c0) at deadlock.cpp:53
#8  0x0000000000401c45 in FetchData::pushData (this=0xeab028) at deadlock.cpp:109
#9  0x0000000000405414 in std::_Mem_fn::_M_call<:shared_ptr>>(std::shared_ptr&&, void const volatile*) const (this=0xead6b0, 
    __ptr=) at /usr/include/c++/4.8.2/functional:558
#10 0x00000000004053a8 in std::_Mem_fn::operator()<:shared_ptr>, , void>(std::shared_ptr&&) const (this=0xead6b0, 
    __object=) at /usr/include/c++/4.8.2/functional:610
#11 0x0000000000405261 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0xead6a0)
    at /usr/include/c++/4.8.2/functional:1732
#12 0x00000000004050b1 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::operator()() (this=0xead6a0) at /usr/include/c++/4.8.2/functional:1720
#13 0x0000000000404f90 in std::thread::_Impl<:_bind_simple> (std::shared_ptr)> >::_M_run() (this=0xead688)
    at /usr/include/c++/4.8.2/thread:115
#14 0x00007fdc577342b0 in ?? () from /usr/lib64/libstdc++.so.6
#15 0x00007fdc5798ee25 in start_thread () from /usr/lib64/libpthread.so.0
#16 0x00007fdc56e9c34d in clone () from /usr/lib64/libc.so.6

Thread 2 (Thread 0x7fdc5023f700 (LWP 18736)):
#0  0x00007fdc5799542d in __lll_lock_wait () from /usr/lib64/libpthread.so.0
#1  0x00007fdc57990dcb in _L_lock_812 () from /usr/lib64/libpthread.so.0
#2  0x00007fdc57990c98 in pthread_mutex_lock () from /usr/lib64/libpthread.so.0
#3  0x00000000004010dc in __gthread_mutex_lock (__mutex=0xeab070) at /usr/include/c++/4.8.2/x86_64-redhat-linux/bits/gthr-default.h:748
#4  0x000000000040141e in std::mutex::lock (this=0xeab070) at /usr/include/c++/4.8.2/mutex:134
#5  0x0000000000402575 in std::unique_lock<:mutex>::lock (this=0x7fdc5023ed90) at /usr/include/c++/4.8.2/mutex:511
#6  0x0000000000401f99 in std::unique_lock<:mutex>::unique_lock (this=0x7fdc5023ed90, __m=...) at /usr/include/c++/4.8.2/mutex:443
#7  0x00000000004019f1 in DataPool::popFromPool (this=0xeab058, outData=std::shared_ptr (count 2, weak 0) 0x7fdc400008c0) at deadlock.cpp:68
#8  0x0000000000401d07 in FetchData::popData (this=0xeab028) at deadlock.cpp:119
#9  0x0000000000405414 in std::_Mem_fn::_M_call<:shared_ptr>>(std::shared_ptr&&, void const volatile*) const (this=0xead960, 
    __ptr=) at /usr/include/c++/4.8.2/functional:558
#10 0x00000000004053a8 in std::_Mem_fn::operator()<:shared_ptr>, , void>(std::shared_ptr&&) const (this=0xead960, 
    __object=) at /usr/include/c++/4.8.2/functional:610
#11 0x0000000000405261 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0xead950)
---Type  to continue, or q  to quit---
    at /usr/include/c++/4.8.2/functional:1732
#12 0x00000000004050b1 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::operator()() (this=0xead950) at /usr/include/c++/4.8.2/functional:1720
#13 0x0000000000404f90 in std::thread::_Impl<:_bind_simple> (std::shared_ptr)> >::_M_run() (this=0xead938)
    at /usr/include/c++/4.8.2/thread:115
#14 0x00007fdc577342b0 in ?? () from /usr/lib64/libstdc++.so.6
#15 0x00007fdc5798ee25 in start_thread () from /usr/lib64/libpthread.so.0
#16 0x00007fdc56e9c34d in clone () from /usr/lib64/libc.so.6

Thread 1 (Thread 0x7fdc57d9e740 (LWP 18734)):
#0  0x00007fdc5798ff57 in pthread_join () from /usr/lib64/libpthread.so.0
#1  0x00007fdc57734077 in std::thread::join() () from /usr/lib64/libstdc++.so.6
#2  0x0000000000401292 in main () at deadlock.cpp:131
(gdb) 
(gdb) 

6.       2 3      lock_wait       ,               

7. info threads         ,       gdb      ,          ,*         
(gdb) info threads
  Id   Target Id         Frame 
  3    Thread 0x7fdc50a40700 (LWP 18735) 0x00007fdc5799542d in __lll_lock_wait () from         /usr/lib64/libpthread.so.0
  2    Thread 0x7fdc5023f700 (LWP 18736) 0x00007fdc5799542d in __lll_lock_wait () from /usr/lib64/libpthread.so.0
* 1    Thread 0x7fdc57d9e740 (LWP 18734) 0x00007fdc5798ff57 in pthread_join () from /usr/lib64/libpthread.so.0
(gdb) 

8.   thread ID          ,      ,Owner             ,     lwp ,    info threads  ,        3,       
(gdb) thread 3
[Switching to thread 3 (Thread 0x7fdc50a40700 (LWP 18735))]
#0  0x00007fdc5799542d in __lll_lock_wait () from /usr/lib64/libpthread.so.0
(gdb) bt
#0  0x00007fdc5799542d in __lll_lock_wait () from /usr/lib64/libpthread.so.0
#1  0x00007fdc57990dcb in _L_lock_812 () from /usr/lib64/libpthread.so.0
#2  0x00007fdc57990c98 in pthread_mutex_lock () from /usr/lib64/libpthread.so.0
#3  0x00000000004010dc in __gthread_mutex_lock (__mutex=0xeab098) at /usr/include/c++/4.8.2/x86_64-redhat-linux/bits/gthr-default.h:748
#4  0x000000000040141e in std::mutex::lock (this=0xeab098) at /usr/include/c++/4.8.2/mutex:134
#5  0x0000000000402575 in std::unique_lock<:mutex>::lock (this=0x7fdc50a3fd80) at /usr/include/c++/4.8.2/mutex:511
#6  0x0000000000401f99 in std::unique_lock<:mutex>::unique_lock (this=0x7fdc50a3fd80, __m=...) at /usr/include/c++/4.8.2/mutex:443
#7  0x000000000040183f in DataPool::pushToPool (this=0xeab058, inputData=std::shared_ptr (count 2, weak 0) 0x7fdc480008c0) at deadlock.cpp:53
#8  0x0000000000401c45 in FetchData::pushData (this=0xeab028) at deadlock.cpp:109
#9  0x0000000000405414 in std::_Mem_fn::_M_call<:shared_ptr>>(std::shared_ptr&&, void const volatile*) const (this=0xead6b0, 
    __ptr=) at /usr/include/c++/4.8.2/functional:558
#10 0x00000000004053a8 in std::_Mem_fn::operator()<:shared_ptr>, , void>(std::shared_ptr&&) const (this=0xead6b0, 
    __object=) at /usr/include/c++/4.8.2/functional:610
#11 0x0000000000405261 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0xead6a0)
    at /usr/include/c++/4.8.2/functional:1732
#12 0x00000000004050b1 in std::_Bind_simple<:_mem_fn> (std::shared_ptr)>::operator()() (this=0xead6a0) at /usr/include/c++/4.8.2/functional:1720
#13 0x0000000000404f90 in std::thread::_Impl<:_bind_simple> (std::shared_ptr)> >::_M_run() (this=0xead688)
    at /usr/include/c++/4.8.2/thread:115
#14 0x00007fdc577342b0 in ?? () from /usr/lib64/libstdc++.so.6
#15 0x00007fdc5798ee25 in start_thread () from /usr/lib64/libpthread.so.0
#16 0x00007fdc56e9c34d in clone () from /usr/lib64/libc.so.6
(gdb) 

9.                53 ,    7 ,    frame     7     
(gdb) frame 7
#7  0x000000000040183f in DataPool::pushToPool (this=0xeab058, inputData=std::shared_ptr (count 2, weak 0) 0x7fdc480008c0) at deadlock.cpp:53
53			std::unique_lock<:mutex> lckTwo(mutexTwo_);
(gdb) 

10.     lckTwo    mutexTwo_   
(gdb) p lckTwo 
$1 = {_M_device = 0xeab098, _M_owns = false}
(gdb) p mutexTwo_ 
$2 = {<:__mutex_base> = {_M_mutex = {__data = {__lock = 2, __count = 0, __owner = 18736, __nusers = 1, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}}, 
      __size = "\002\000\000\000\000\000\000\000\060I\000\000\001", '\000' , __align = 2}}, }
(gdb) 
#           3      lckTwo   ,   mutexTwo_   lwp  18736     ,   2,                  ,      2     ,          。                ,    。              ,     gdb    core dump     。