在工作項目使用多進程、多線程過程中,因争奪資源而造成一種資源競态,所以需加鎖處理。如下圖所示,線程A想獲取線程B的鎖,線程B想獲取線程C的鎖,線程 C 想獲取線程D的鎖, 線程D想獲取線程A的鎖,從而構建了一個資源獲取環,當進程或者線程申請的鎖處于相互交叉鎖住的情況,就會出現死鎖,它們将無法繼續運行。
死鎖的存在是因為有資源獲取環的存在,所以隻要能檢測出資源獲取環,就等同于檢測出死鎖的存在。
二、原理在不改變項目源代碼的情況下,采用圖算法來檢測環的存在,使用有向圖來存儲;如線程A獲取線程B已占用的鎖(表示線程B獲取鎖成功),則為線程A指向線程B;啟動一個線程定時對圖進行檢測是否有環的存在。
(1)數據結構
//數據/點
struct node{
uint64 thread_id;//線程ID
uint64 lock_id;//鎖ID
int degress;
};
//數據和數據結構分開
struct vertex{
struct node *d;
struct vertex *next;
};
struct graph{
struct vertex list[THREAD_MAX];//存儲圖的所有節點
int num;//已經使用了多少個
struct node locklist[THREAD_MAX];
int lockidx;
pthread_mutex_t mutex;//預留:線程安全考慮,在對圖修改時加鎖
};
a.創建圖節點
//創建圖節點
struct vertex *create_vertex(struct node *d){
struct vertex *tex = (struct vertex*)calloc(1,sizeof(struct vertex));
if(tex == NULL) return NULL;
tex->d = d;
tex->next = NULL;
return tex;
}
b.查找節點
//查找節點,是否存在
int search_vertex(struct node *d){
int i;
for (i = 0; i < tg->num; i )
{
if (tg->list[i].d->thread_id == d->thread_id)
{
return i;
}
}
c.添加節點
//添加節點,隻是把添加的節點放到list中,還沒有确定各節點間的指向,必須通過add_edge添加邊來确定
void add_vertex(struct node *d){
if (search_vertex(d) == -1)
{
tg->list[tg->num].d = d;//添加到list中
tg->list[tg->num].next = NULL;
tg->num ;//節點數累加
}
}
d.添加邊,指定方向
//添加邊,指定方向,誰指向誰
void add_edge(struct node *from, struct node *to){
add_vertex(from);
add_vertex(to);
struct vertex *v = &tg->list[search_vertex(from)];
while (v->next != NULL)
{
v = v->next;
}
v->next = create_vertex(to);
}
e.檢測節點間是否有邊
//檢測節點from和to間是否有邊連接
int verifty_edge(struct node *from, struct node *to){
if(tg->num == 0) return 0;
int idx = search_vertex(from);
if(idx == -1) return 0;
struct vertex *v = &(tg->list[idx]);
while(v != NULL){
if(v->d->thread_id == to->thread_id) return 1;
v = v->next;
}
return 0;
}
f.删除邊
//删除邊
void remove_edge(struct node *from, struct node *to){
int idxi = search_vertex(from);
int idxj = search_vertex(to);
if(idxi != -1 && idxj !=-1){
struct vertex *v = &tg->list[idxi];
struct vertex *remove;
while(v->next != NULL){
if(v->next->d->thread_id == to->thread_id){//找到要删除的節點
remove = v->next;
v->next = v->next->next;
free(remove);
break;
}
v = v->next;
}
}
}
本文采用圖遍曆中最為常用的深度優先搜索進行遍曆,代碼如下。
//dfs深度遍曆
int dfs(int idx){
struct vertex *v = &tg->list[idx];
if(visited[idx] == 1){//有環
path[k ] = idx;
print_deadlock();
deadlock = 1;
return 0;
}
visited[idx] =1;//被遍曆到了,賦值為1,保證同一個節點隻能遍曆一次
path[k ] = idx;
while(v->next !=NULL){
dfs(search_vertex(v->next->d));
k--;
v = v->next;
}
return 1;
}
//遍曆圖,任意從圖的一個節點出發,對每一個節點進行dfs遍曆
int search_for_cycle(int idx){
struct vertex *v = &tg->list[idx];
visited[idx] = 1;
k = 0;
path[k ] = idx;
while(v->next != NULL){
int i = 0;
for (; i < tg->num; i )
{
if(i == idx){
continue;
}
visited[i] = 0;
}
for(i = 1; i <= THREAD_MAX; i ){
path[i] = -1;
}
k = 1;
dfs(search_vertex(v->next->d));
v = v->next;
}
}
C 後台開發架構師免費學習地址:C/C Linux服務器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂
另外還整理一些C 後台開發架構師 相關學習資料,面試題,教學視頻,以及學習路線圖,免費分享有需要的可以自行添加點擊 正在跳轉 群文件共享
啟動線程定時檢測圖是否有環,代碼如下。
//從第0個節點開始dfs
void check_dead_lock(){
int i = 0;
deadlock = 0;
for(;i < tg->num; i ){
if(deadlock == 1) break;
search_for_cycle(i);
}
if(deadlock == 0){
printf("no deadlock\n");
}
}
//檢測鎖線程func
static void *thread_func(void *args){
while(1){
sleep(5);
check_dead_lock();
}
}
//啟動檢測鎖線程
void start_check(){
tg = (struct graph*)malloc(sizeof(struct graph));
tg->num = 0;
tg->lockidx = 0;
pthread_t tid;
pthread_create(&tid,NULL,thread_func,NULL);
}
為了不改變項目原代碼,使用hook在應用程序調用系統加鎖、解鎖API時進行劫持,使其實際調用的是應用程序定義的加鎖、解鎖API;再進行加鎖、解鎖前,我們先去理解3個狀态,加鎖前、加鎖後、解鎖後,即:lock_before、lock_after、unlock_after,通過這三個函數與圖構建起來,具體實現如下。
//1.沒有被其他線程占用,不用處理
//2.有被其它線程占用,就要把邊構建起來
// 添加邊
void lock_before(uint64 thread_id, uint64 lockid){
int idx = 0;
for(;idx < tg->lockidx;idx ){
if(tg->locklist[idx].lock_id == lockid){
struct node from;
from.thread_id = thread_id;
add_vertex(&from);
struct node to;
to.thread_id = tg->locklist[idx].thread_id;
tg->locklist[idx].degress ;
add_vertex(&to);
if(!verifty_edge(&from, &to)){
add_edge(&from, &to);//添加邊
}
}
}
}
//1.沒有被其它線程占用
//先加入一個節點add_edge
//2.有被占用
//是進不來lock_after的
//
//等unlock_after 釋放後
// mtx沒有主人
void lock_after(uint64 threadid, uint64 lockid) {
int idx = 0;
if(-1 == (idx = search_lock(lockid))){
int eidx = search_empty_lock();
tg->locklist[eidx].thread_id = threadid;
tg->locklist[eidx].lock_id = lockid;
inc(&tg->lockidx, 1);
}else{
struct node from;
from.thread_id = threadid;
struct node to;
to.thread_id = tg->locklist[idx].thread_id;
tg->locklist[idx].degress--;
if(verifty_edge(&from, &to)){
remove_edge(&from, &to);//不在死鎖檢測的圈裡面了,所以删除邊
}
tg->locklist[idx].thread_id = threadid;
}
}
void unlock_after(uint64 threadid, uint64 lockid) {
int idx = search_lock(lockid);
if(tg->locklist[idx].degress == 0){
tg->locklist[idx].thread_id = 0;
tg->locklist[idx].lock_id = 0;
}
}
honk鈎子主要實現pthread_mutex_lock、pthread_mutex_unlock的劫持,具體實現如下。
int pthread_mutex_lock(pthread_mutex_t *mutex){
pthread_t selfid = pthread_self();
lock_before(selfid, (uint64)mutex);
pthread_mutex_lock_f(mutex);//執行系統加鎖的入口函數
lock_after(selfid, (uint64)mutex);
}
int pthread_mutex_unlock(pthread_mutex_t * mutex){
pthread_t selfid = pthread_self();
pthread_mutex_unlock_f(mutex);//執行系統解鎖的入口函數
unlock_after(selfid, (uint64)mutex);
}
static int init_hook(){
pthread_mutex_lock_f = dlsym(RTLD_NEXT,"pthread_mutex_lock");
pthread_mutex_unlock_f = dlsym(RTLD_NEXT,"pthread_mutex_unlock");
}
//測試樣例
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 *th_func1(void *arg) {
pthread_mutex_lock(&mtx1);
sleep(1);
pthread_mutex_lock(&mtx2);
pthread_mutex_unlock(&mtx2);
pthread_mutex_unlock(&mtx1);
}
void *th_func2(void *arg) {
pthread_mutex_lock(&mtx2);
sleep(1);
pthread_mutex_lock(&mtx3);
pthread_mutex_unlock(&mtx3);
pthread_mutex_unlock(&mtx2);
}
void *th_func3(void *arg) {
pthread_mutex_lock(&mtx3);
sleep(1);
pthread_mutex_lock(&mtx1);
pthread_mutex_unlock(&mtx1);
pthread_mutex_unlock(&mtx3);
}
void *th_func4(void *arg) {
pthread_mutex_lock(&mtx2);
sleep(1);
pthread_mutex_lock(&mtx3);
pthread_mutex_unlock(&mtx3);
pthread_mutex_unlock(&mtx2);
}
int main(){
init_hook();//初始化hook
start_check();//啟動檢測死鎖線程
pthread_t t1,t2,t3,t4;
pthread_create(&t1,NULL,th_func1,NULL);
pthread_create(&t2,NULL,th_func2,NULL);
pthread_create(&t3,NULL,th_func3,NULL);
pthread_create(&t4,NULL,th_func4,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
pthread_join(t4,NULL);
return 0;
}
原文地址:死鎖檢測實現 - MrJuJu - 博客園
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!