ceph-depth检测算法

死锁类错误,采用lockdep算法能够检测出来。一般情况下,死锁分如下几种情况:

1) AA:重复上锁。

2) ABBA:T1线程使用AB顺序上锁,同时T2线程使用BA顺序上锁,导致上锁成环形成死锁。

3) ABBCCA:T1线程使用AB顺序上锁,同时T2线程使用BC顺序上锁,T3线程使用CA顺序上锁,导致上锁成环形成死锁。

4) 重复unlock。

ceph中自带的lockdep功能,可以实现如上几种死锁检测,本文探讨其检测原理。

该算法目前暂不支持自旋锁和回环锁,后续继续开发支持。

算法检测步骤如下:

算法检测流程图.jpg

核心算法:follows跟踪状态。

该follows集合采用二维数组的形式,使用位运算,记录锁的跟踪状态。follows集合的定义是:

follows[lock1_id][lock2_id/8] = 1<<(lock2_id%8)

每个锁都拥有一个唯一的id号,该id号在锁初始化时或调用Lock时注册。另有一个held二维数组,用于记录线程号和锁id之间的关系,其中含有backtrace可以记录当前时刻的函数堆栈,但为了性能一般不记录,只有检测到死锁时才会记录。held的定义如下:

held[pthread_t][lock1_id] = BackTrace;

其中某线程第一次上锁时会将自己的线程号和正在执行上锁的锁id先加入到held中,当该线程进行第二次上锁时,会从held中取出其中的锁id记录。之后,通过如上follows的定义逻辑检查是否成环,成环表示死锁,不成环则表示是新的锁关系,更新到follows中。

检测方式

重复上锁(AA)

步骤1:

T1(pthread_t: 100000)

lockA.Lock() (id: 1)

算法检测:

1) 首次加入:held[100000][1] = 0

步骤2:

T1(pthread_t: 100000)

lockA.Lock() (id: 1)

算法检测:

1) 取出held[100000] = (1, 0) 即lockA_id: 1

2) 检查当前上锁id与head[100000] 即1==1,触发重复上锁断言,打印堆栈。

ABBA成环

步骤1:

T1(pthread_t: 100000)

lockA.Lock() (id: 1)

算法检测:

1) 首次加入:held[100000][1] = 0

步骤2:

T2(pthread_t: 100001)

lockB.Lock() (id: 2)

算法检测:

1) 首次加入:held[100001][2] = 0

步骤3:

T1(pthread_t: 100000)

lockB.Lock() (id: 2)

算法检测:

1) 取出held[100000] = (1, 0) 即lockA_id: 1

2) 检查是否已有follows记录及状态:follows[2][1/8] & (1<<(1%8)) == 0,即没有记录。

3) 新建当前follows记录:follows[1][2/8] |= (1<<(2%8)),即follows[1][0] |= (1<<2)。

步骤4:

T2(pthread_t: 100001)

lockA.lock() (id: 1)

算法检测:

1) 取出held[100001] = (2, 0) 即lockB_id: 2

2) 检查是否已有follows记录及状态:follows[1][2/8] & (1<<(2%8)) == 1,即检测成环。

3) 新建当前调用链堆栈BackTrace,打印并断言。

ABBCCA成环

步骤1:

T1(pthread_t: 100000)

lockA.Lock() (id: 1)

算法检测:

1) 首次加入:held[100000][1] = 0

步骤2:

T2(pthread_t: 100001)

lockB.Lock() (id: 2)

算法检测:

1) 首次加入:held[100001][2] = 0

步骤3:

T3(pthread_t: 100002)

lockC.Lock() (id: 3)

算法检测:

1) 首次加入:held[100002][3] = 0

步骤4:

T1(pthread_t: 100000)

lockB.Lock() (id: 2)

算法检测:

1) 取出held[100000] = (1, 0) 即lockA_id: 1

2) 检查是否已有follows记录及状态:follows[2][1/8] & (1<<(1%8)) == 0,即没有记录。

3) 新建当前follows记录:follows[1][2/8] |= (1<<(2%8)),即follows[1][0] |= (1<<2)。

步骤5:

T2(pthread_t: 100001)

lockC.Lock() (id: 3)

算法检测:

1) 取出held[100001] = (2, 0) 即lockB_id: 2

2) 检查是否已有follows记录及状态:follows[3][2/8] & (1<<(2%8)) == 0,即没有记录。

3) 新建当前follows记录:follows[2][3/8] |= (1<<(3%8)),即follows[2][0] |= (1<<3)。

步骤6:

T3(pthread_t: 100002)

lockA.lock() (id: 1)

算法检测:

4) 取出held[100002] = (3, 0) 即lockB_id: 3

5) 检查是否已有follows记录及状态:follows[1][3/8] & (1<<(3%8)),即follows[1][0] & (1<<3) == 0,有记录但未成环。

6) 检查当前锁相关历史记录:

follows[1][1/8] & (1<<(1%8))即follows[1][0] & (1<<1) == 0,有记录但未成环。

follows[1][2/8] & (1<<(2%8))即follows[1][0] & (1<<2) == 1,有记录且成环。

7) 新建当前调用链堆栈BackTrace,打印并断言。

重复放锁

这个在重复调用pthread_mutex_unlock就会返回非0错误码,直接断言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值