手機(jī)app微信網(wǎng)站建設(shè)磁力狗在線搜索
死鎖檢測(cè)組件-設(shè)想
現(xiàn)在有三個(gè)臨界資源和三把鎖綁定了,三把鎖又分別被三個(gè)線程占用。(不用關(guān)注臨界資源,因?yàn)殒i和臨界資源是綁定的)

但現(xiàn)在出現(xiàn)這種情況:線程1去申請(qǐng)獲取鎖2,線程2申請(qǐng)獲取鎖3,線程3申請(qǐng)獲取鎖1,這樣就會(huì)造成死鎖:

死鎖問(wèn)題,可轉(zhuǎn)換為有向圖的環(huán)路檢測(cè)
死鎖的構(gòu)建
有四個(gè)線程,4把鎖,以下代碼一定會(huì)產(chǎn)生死鎖
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx4 = PTHREAD_MUTEX_INITIALIZER;void *thread_routine_a(void *arg) {printf("thread_routine a \n");pthread_mutex_lock(&mtx1);sleep(1); pthread_mutex_lock(&mtx2);pthread_mutex_unlock(&mtx2);pthread_mutex_unlock(&mtx1);printf("thread_routine a exit\n");}void *thread_routine_b(void *arg) {printf("thread_routine b \n");pthread_mutex_lock(&mtx2);sleep(1);pthread_mutex_lock(&mtx3);pthread_mutex_unlock(&mtx3);pthread_mutex_unlock(&mtx2);printf("thread_routine b exit \n");
// -----pthread_mutex_lock(&mtx1);
}void *thread_routine_c(void *arg) {printf("thread_routine c \n");pthread_mutex_lock(&mtx3);sleep(1);pthread_mutex_lock(&mtx4);pthread_mutex_unlock(&mtx4);pthread_mutex_unlock(&mtx3);printf("thread_routine c exit \n");
}void *thread_routine_d(void *arg) {printf("thread_routine d \n");pthread_mutex_lock(&mtx4);sleep(1);pthread_mutex_lock(&mtx1);pthread_mutex_unlock(&mtx1);pthread_mutex_unlock(&mtx4);printf("thread_routine d exit \n");
}int main() {
#if 1init_hook();pthread_t tid1, tid2, tid3, tid4;pthread_create(&tid1, NULL, thread_routine_a, NULL);pthread_create(&tid2, NULL, thread_routine_b, NULL);pthread_create(&tid3, NULL, thread_routine_c, NULL);pthread_create(&tid4, NULL, thread_routine_d, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);pthread_join(tid4, NULL);
}
這里產(chǎn)生了:線程a => 線程b => 線程c => 線程d =》 線程a的這樣一個(gè)環(huán)路
但是我們不知道哪把鎖被哪個(gè)線程占用了,沒(méi)法構(gòu)建有向圖,也就無(wú)法得知是否產(chǎn)生了這樣一個(gè)環(huán)路
這時(shí),可以用hook,調(diào)自己寫的 pthread_mutex_lock,將線程和鎖的映射關(guān)系保存起來(lái)
pthread的hook
有點(diǎn)像裝飾器模式
// 函數(shù)指針
typedef int (*pthread_mutex_lock_t)(pthread_mutex_t *mutex);
pthread_mutex_lock_t pthread_mutex_lock_f;typedef int (*pthread_mutex_unlock_t)(pthread_mutex_t *mutex);
pthread_mutex_unlock_t pthread_mutex_unlock_f;static int init_hook() {// RTLD_NEXT可以理解為代碼段,在這里面找pthread_mutex_lock函數(shù)名,把地址返回// 所以pthread_mutex_lock_f就是靜態(tài)或動(dòng)態(tài)庫(kù)里的pthread_mutex_lock鎖函數(shù) pthread_mutex_lock_f = dlsym(RTLD_NEXT, "pthread_mutex_lock"); pthread_mutex_unlock_f = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
}int pthread_mutex_lock(pthread_mutex_t *mutex) {printf("pthread_mutex_lock selfid %ld, mutex: %p\n", pthread_self(), mutex);beforelock(pthread_self(), mutex);pthread_mutex_lock_f(mutex); // 用鉤子的好處,給系統(tǒng)函數(shù)命一個(gè)別名afterlock(pthread_self(), mutex);}int pthread_mutex_unlock(pthread_mutex_t *mutex) {printf("pthread_mutex_unlock\n");pthread_mutex_unlock_f(mutex);afterunlock(pthread_self(), mutex);}
這樣,在加鎖的時(shí)候,就能知道鎖id(mutex)和線程id的對(duì)應(yīng)關(guān)系
圖的構(gòu)建
通過(guò)鄰接表實(shí)現(xiàn)有向圖,如:線程1等待線程2釋放鎖,則將線程id2掛到線程1后面

而如何知道線程1申請(qǐng)的鎖被線程2占用了呢:設(shè)置一個(gè) mutex 和 thread的映射列表(結(jié)構(gòu)體數(shù)組),通過(guò)mutex返回threadid。
如線程1對(duì)mutex1加鎖,將(mutex1,threadid1)加入locklist中,表示mutex1被線程1占用了
圖的鄰接表生成:當(dāng)前線程根據(jù)要申請(qǐng)的mutex a找到占用這把鎖的線程id A(通過(guò)映射列表),將改線程追加到自己的后面
通過(guò)鄰接表檢測(cè)環(huán)路:DFS,檢測(cè)過(guò)的標(biāo)為1,再遇到一個(gè)1就表示死鎖了

那我們?cè)趺窗堰@個(gè)圖構(gòu)建起來(lái): 通過(guò)三個(gè)原語(yǔ)操作
beforelock、afterlock、afterrunlock
以后要用到再說(shuō)吧
至此,死鎖檢測(cè)組件設(shè)計(jì)的大致思路為:通過(guò)hook保存muitex和threadid的映射關(guān)系,根據(jù)這個(gè)映射關(guān)系生成線程之間的有向圖(鄰接表),再利用dfs檢測(cè)圖的環(huán)路