衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

死鎖的現象及原理

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-10 16:32 ? 次閱讀

組件如何放入自己的項目里?把代碼末兩個Debug部分刪除,在你的項目里添加下面兩句代碼即可使用死鎖檢測組件。

init_hook();
start_check();

1. 死鎖的現象以及原理

1.1 復現最簡單的死鎖

線程A占有鎖1,線程B占有鎖2;此時線程A想要獲取鎖2,但是鎖2已經被線程B占有, 此時線程A會休眠等待線程B釋放鎖2后,再去獲得鎖2。可以看到下面的場景,線程B想要獲取鎖1,結果線程B也休眠去了。這就導致死鎖,鎖1和鎖2永遠得不到釋放,因為線程A和線程B都在等待另一個鎖的釋放。這種僵持的狀態,就稱為死鎖。

圖片

正如下面代碼所示,這樣就引發了死鎖

void *thread_rountine_1(void *args) {
pthread_t selfid = pthread_self();
printf("thread_routine 1 : %ld n", selfid);
pthread_mutex_lock(&mutex_1);
sleep(1);
pthread_mutex_lock(&mutex_2);
pthread_mutex_unlock(&mutex_2);
pthread_mutex_unlock(&mutex_1);
return (void *) (0);
}
void *thread_rountine_2(void *args) {
pthread_t selfid = pthread_self(); //
printf("thread_routine 2 : %ld n", selfid);
pthread_mutex_lock(&mutex_2);
sleep(1);
pthread_mutex_lock(&mutex_1);
pthread_mutex_unlock(&mutex_1);
pthread_mutex_unlock(&mutex_2);
return (void *) (0);
}

1.2 從死鎖中找出檢測死鎖的規律

我們來看看下面這張圖,線程A想要獲取線程B的資源,線程B想要獲取線程C的資源,線程C想要獲取線程D的資源,線程D想要獲取線程A的資源,這其實就構成了一個有向圖的環路

來看看前面介紹的最簡單的死鎖,發現其本直也是構成了一個有向圖的環路

圖片

來看看非死鎖的場景,只要線程D釋放了mutex4,那么線程C就能獲得鎖,隨后線程C釋放mutex3和4,那么線程B…可以發現,這個非死鎖的場景,它是一個有向圖,但這個圖沒有構成環路。

圖片

過上面三個場景的分析,我們其實就可以把死鎖的問題,轉換為 有向圖的環路檢測。在線程進行加鎖前,我們去判斷一下所有的線程有沒有構成環路,如果有,則說明現在很有可能會進入死鎖。

2. 檢測死鎖的前置條件

2.1 有向圖的邊怎么來?

我們現在已經知道了死鎖的問題,就轉換為 有向圖的環路檢測。那么這個有向圖怎么構建?在我們對mutex1加鎖的時候,我們怎么知道是線程A占有mutex1,在對mutex2加鎖的時候,怎么知道它已經被線程B占有了?我們無法知道鎖是屬于哪個線程的。既然連鎖都不知道屬于哪個線程,哪有如何構建出有向圖呢?換言之,我們需要解決:知道當前鎖被哪個線程占用。我們不知道的原因很簡單,就是mutex和pthread_id沒有一個對應關系。

//鎖與線程的信息
struct pair_t {
    unsigned long int th_id;
    enum Type type;

    unsigned long int lock_id;
    int degress;
};

圖片

我們可以做出一個數據結構,在加鎖之前,判斷這個鎖有沒有被別的線程使用,如果沒有,在加鎖之后我們將這個鎖與本線程綁定,做一個pair,然后把這個pair存起來。比如說線程線程A和mutex1綁定,線程B和mutex2綁定了。當線程A再次去嘗試對mutex2加鎖之前,先判斷mutex2是否名花有主?如果有,那有向圖的邊不就來了嗎?不知道讀者有沒有注意到,這一段話都建立在加鎖之前判斷 鎖 是否名花有主。

有一個非常簡單粗暴的方法,在加鎖之前調用一個函數,加鎖之后調用一個函數。讀者可以想一下,本文是要實現一個組件,所謂組件,給別人也能用,難道在一個項目里面,想要檢測一下死鎖,去把lock上下全部加兩個函數?這顯然不符合我們組件的設想,我們希望不改變別人的代碼,就能實現檢測。

lock_before(self_id, (unsigned long int) mutex);
pthread_mutex_lock(&mutex);
lock_after(self_id, (unsigned long int) mutex);

要想實現上面的需求,我們可以使用hook。

2.2 hook—>dlsym

hook是什么意思?鉤子,簡單來說,我們使用hook,可以把系統或第三方庫提供的函數,替換成我們寫的同名函數,而第三方庫的函數則被我們改名,在我們寫的同名函數里,可以去調用第三方庫原來的函數。

正如下面代碼所示,系統提供的pthread_mutex_lock被改名為pthread_mutex_lock_f。那么我們就可以使用pthread_mutex_lock來當作函數名稱,如此一來,在別的項目里面,我們通過hook就可以進行死鎖檢測,而不需要去改代碼了。

hook提供了兩個接口;1. dlsym()是針對系統的,系統原始的api。2. dlopen()是針對第三方的庫。

/* ******* ******************Hook****************** ******* */

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() {
    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) {
    pthread_t self_id = pthread_self(); //
    lock_before(self_id, (unsigned long int) mutex);
    pthread_mutex_lock_f(mutex);
    lock_after(self_id, (unsigned long int) mutex);
}

在進程的虛擬內存空間里面,有一塊代碼段 ,上面代碼中,pthread_mutex_lock_f是一個函數指針,實際上,就是把pthread_mutex_lock_f指向代碼段里系統函數的入口地址 ,以此來實現偷天換日。

還需要注意一點,這個#define _GNU_SOURCE要寫在前面,因為這個就相當于一個開關,在下面的.h文件里面,有#ifdef _GNU_SOURCE的地方。在gcc編譯的時候后面加上 -ldl。

#define _GNU_SOURCE
#include < dlfcn.h >

3. 有向圖

3.1 有向圖的數據結構

下面來看一下結構體的含義

圖片

ertex_list的每一項,都是一個頂點,后面鏈表里面存的,都是邊的另一個點。

圖片

vlock_list的每一項,存的都是鎖與線程的信息

圖片

/* ******* ******************Digraph****************** ******* */

enum Type {
    PROCESS, RESOURCE
};
//鎖與線程的信息
struct pair_t {
    unsigned long int th_id;
    enum Type type;

    unsigned long int lock_id;
    int degress;
};
//頂點
struct vertex_t {
    struct pair_t pair;
    struct vertex_t *next;
};

struct task_graph {
    struct vertex_t vertex_list[MAX];
    int vertex_num;

    struct pair_t lock_list[MAX];
    int lock_num;

    pthread_mutex_t mutex;

    int path[MAX + 1];
    int visited[MAX];
    int k;
    int deadlock;
};

struct task_graph *tg = NULL;


//創建一個vertex
struct vertex_t *create_vertex(struct pair_t pair) {
    struct vertex_t *tex = (struct vertex_t *) malloc(sizeof(struct vertex_t));

    tex- >pair = pair;
    tex- >next = NULL;
    return tex;
}

//查找vertex在list里面的下標
int search_vertex(struct pair_t pair) {
    int i = 0;

    for (i = 0; i < tg- >vertex_num; i++) {
        if (tg- >vertex_list[i].pair.type == pair.type && tg- >vertex_list[i].pair.th_id == pair.th_id) {
            return i;
        }
    }

    return -1;
}

//把vertex添加到vertex_list里面
void add_vertex(struct pair_t pair) {
    if (search_vertex(pair) == -1) {
        tg- >vertex_list[tg- >vertex_num].pair = pair;
        tg- >vertex_list[tg- >vertex_num].next = NULL;
        tg- >vertex_num++;
    }
}

//添加邊,把v添加到u的鏈表里
int add_edge(struct pair_t u, struct pair_t v) {
    add_vertex(u);
    add_vertex(v);

    struct vertex_t *cnt = &(tg- >vertex_list[search_vertex(u)]);

    while (cnt- >next != NULL) {
        cnt = cnt- >next;
    }

    cnt- >next = create_vertex(v);
}

//檢查邊是否存在
int verify_edge(struct pair_t u, struct pair_t v) {
    if (tg- >vertex_num == 0) return 0;

    int idx = search_vertex(u);
    if (idx == -1) {
        return 0;
    }

    struct vertex_t *cnt = &(tg- >vertex_list[idx]);

    while (cnt != NULL) {
        if (cnt- >pair.th_id == v.th_id) {
            return 1;
        }
        cnt = cnt- >next;
    }
    return 0;
}

//刪除邊
int remove_edge(struct pair_t u, struct pair_t v) {

    int idx_u = search_vertex(u);
    int idx_v = search_vertex(v);

    if (idx_u != -1 && idx_v != -1) {

        struct vertex_t *cnt = &tg- >vertex_list[idx_u];
        struct vertex_t *remove;

        while (cnt- >next != NULL) {
            if (cnt- >next- >pair.th_id == v.th_id) {
                remove = cnt- >next;
                cnt- >next = cnt- >next- >next;
                free(remove);
                break;
            }
            cnt = cnt- >next;
        }
    }
}

3.2 dfs判斷環的方法

現在邊也處理好了,鎖與線程的關系也處理好了,那么我們如何去判斷有沒有環呢?我們使用DFS來判斷。

圖片

/* ******* ******************check cycle****************** ******* */

//打印
void print_deadlock(void) {
    int i = 0;
    printf("deadlock : ");
    for (i = 0; i < tg- >k - 1; i++) {
        printf("%ld -- > ", tg- >vertex_list[tg- >path[i]].pair.th_id);
    }
    printf("%ldn", tg- >vertex_list[tg- >path[i]].pair.th_id);
}

void print_locklist(void) {
    int i = 0;

    printf("-----------print_locklist----------n");
    for (i = 0; i < tg- >lock_num; i++) {
        printf("threadid : %ld, lockid: %ldn", tg- >lock_list[i].th_id, tg- >lock_list[i].lock_id);
    }
    printf("-----------------------------------n");
}

int DFS(int idx) {
    struct vertex_t *ver = &tg- >vertex_list[idx];
    if (tg- >visited[idx] == 1) {
        tg- >path[tg- >k++] = idx;
        print_deadlock();
        tg- >deadlock = 1;
        return 0;
    }

    tg- >visited[idx] = 1;
    tg- >path[tg- >k++] = idx;

    while (ver- >next != NULL) {
        DFS(search_vertex(ver- >next- >pair));
        tg- >k--;
        ver = ver- >next;
    }

    return 1;
}

//判斷某個頂點是否成環
int search_for_cycle(int idx) {
    struct vertex_t *ver = &tg- >vertex_list[idx];
    tg- >visited[idx] = 1;
    tg- >k = 0;
    tg- >path[tg- >k++] = idx;

    while (ver- >next != NULL) {
        int i = 0;
        for (i = 0; i < tg- >vertex_num; i++) {
            if (i == idx) continue;
            tg- >visited[i] = 0;
        }

        for (i = 1; i <= MAX; i++) {
            tg- >path[i] = -1;
        }
        tg- >k = 1;

        DFS(search_vertex(ver- >next- >pair));
        ver = ver- >next;
    }
}

//檢查是否死鎖
void check_dead_lock(void) {
    printf("-----------check deadlock----------n");

    int i;
    tg- >deadlock = 0;
    for (i = 0; i < tg- >vertex_num; i++) {
        if (tg- >deadlock == 1) {
            break;
        }
        //從每個點都出發一遍
        search_for_cycle(i);
    }
    if (tg- >deadlock == 0) {
        printf("no deadlockn");
    }

    printf("----------------------------------n");
}

3.3 簡單測試一下

可以看到我們的結果與預期一致,說明我們的有向圖與判斷環完成了,那么下面我們就應該去寫上鎖前后的函數了。

/* ******* ******************Debug 2****************** ******* */


int main() {
    tg = (struct task_graph *) malloc(sizeof(struct task_graph));
    tg- >vertex_num = 0;

    struct pair_t v1;
    v1.th_id = 1;
    v1.type = PROCESS;
    add_vertex(v1);

    struct pair_t v2;
    v2.th_id = 2;
    v2.type = PROCESS;
    add_vertex(v2);

    struct pair_t v3;
    v3.th_id = 3;
    v3.type = PROCESS;
    add_vertex(v3);

    struct pair_t v4;
    v4.th_id = 4;
    v4.type = PROCESS;
    add_vertex(v4);


    struct pair_t v5;
    v5.th_id = 5;
    v5.type = PROCESS;
    add_vertex(v5);


    add_edge(v1, v2);
    add_edge(v2, v3);
    add_edge(v3, v4);
    add_edge(v4, v5);
    add_edge(v3, v1);
    add_edge(v5, v1);

    check_dead_lock();
//    search_for_cycle(search_vertex(v1));
}
root@wxf:/tmp/tmp.d4vz2dOyJP# gcc -o deadlock_success deadlock_success.c -lpthread -ldl
root@wxf:/tmp/tmp.d4vz2dOyJP# ./deadlock_success 
-----------check deadlock----------
deadlock : 1 -- > 2 -- > 3 -- > 4 -- > 5 -- > 1
deadlock : 1 -- > 2 -- > 3 -- > 1
----------------------------------
root@wxf:/tmp/tmp.d4vz2dOyJP#

圖片

4. 三個原語操作

現在有向圖和hook都有了,那么我們如何把死鎖檢測出來?換言之,我們怎么使用pthread_mutex_lock和pthread_mutex_unlock構建有向圖?

在調用系統提供的lock以前,我們需要檢測這個鎖有沒有被別的線程占用,如果被占用,那么我們就需要往圖里面加一條邊。

如果沒有被占用,那么我們就往里面走。也就是說加鎖完,調用系統提供的lock之后, 我們需要告訴后面的線程,這個鎖被我占用了,即添加一項pair,供別人lock之前去檢測。 如果被占用了,然后鎖被釋放,本線程獲取到了這個以前被占用的鎖,那么我們lock之后,需要把原來添加的一條邊刪除掉,因為這個鎖已經屬于自己了,并且將鎖對應的pair中的th_id改成自己。

在調用系統提供的unlock之后,解鎖了一個鎖之后,我們去看看還有沒有渴望得到這個鎖的,如果沒有,則將鎖對應的pair置空,如果有,則不管pair。

注意:下面三個函數,我對三個函數都加鎖了,這里是我的偷懶操作,鎖的粒度較大。如果想優化,應該放到serch函數里面,我這里懶得去改了。

int pthread_mutex_lock(pthread_mutex_t *mutex) {
    pthread_t self_id = pthread_self(); 

    lock_before(self_id, (unsigned long int) mutex);
    pthread_mutex_lock_f(mutex);
    lock_after(self_id, (unsigned long int) mutex);
}

int pthread_mutex_unlock(pthread_mutex_t *mutex) {
    pthread_t self_id = pthread_self();

    pthread_mutex_unlock_f(mutex);
    unlock_after(self_id, (unsigned long int) mutex);
}

4.1 lock_before

我們現在把加鎖理解為談戀愛確認關系。在確認關系之前,我們要去看一下這個女生有沒有男朋友,如果她沒有男朋友,妙哉!那么我們就直接確認關系(lock)吧!如果她有男朋友,那現在還不能和她談戀愛,我們先與她曖昧曖昧(add_edge),等著她分手。

void lock_before(unsigned long int thread_id, unsigned long int lock) {
    pthread_mutex_lock_f(&tg- >mutex);

    int idx = search_lock(lock);
//    printf("[lock_before] self_id:%lu lock:%lu lock idx:%d n", thread_id, lock, idx);
    //如果該鎖是第一次則什么都不做
    if (idx != -1) {
        //u是想要加鎖的線程
        struct pair_t u;
        u.th_id = thread_id;
        u.type = PROCESS;
        //把vertex添加到vertex_list里面
        add_vertex(u);
        //v是鎖原來的線程
        struct pair_t v;
        v.th_id = tg- >lock_list[idx].th_id;
        tg- >lock_list[idx].degress++;
        v.type = PROCESS;
        add_vertex(v);

        if (!verify_edge(u, v)) {
            add_edge(u, v); // 把v加入到vertex_list的u的鏈表中
        }
    }

    pthread_mutex_unlock_f(&tg- >mutex);
}

4.2 lock_after

現在我們加鎖完了,也就是談戀愛確認關系了之后,如果我們是她的初戀,那么我們要向全世界宣布(tg->lock_list[empty_lock_idx]):她,是我的女人!如果不是初戀,她被別人宣布過了,那我們就別搞這么浪漫了,把她給我們的備注改成男朋友就好了(tg->lock_list[idx].th_id = thread_id;),并且我們也不需要曖昧聊天了(remove_edge),因為她已經是我們女朋友了。

void lock_after(unsigned long int thread_id, unsigned long int lock) {
    pthread_mutex_lock_f(&tg- >mutex);

    int idx = search_lock(lock);
//    printf("[lock_after ] self_id:%lu lock:%lu ", thread_id, lock);

    if (idx == -1) {  // 第一次加鎖,找一個空位lock_list,設置th_id和lock
        int empty_lock_idx = search_empty_lock(lock);
        tg- >lock_list[empty_lock_idx].th_id = thread_id;
        tg- >lock_list[empty_lock_idx].lock_id = lock;
//        printf("分配lock_list位置 idx:%d n", empty_lock_idx);
        if (empty_lock_idx >= tg- >lock_num) {
            inc(&tg- >lock_num, 1);
        }
    }
    else {
        //u是想要加鎖的線程
        struct pair_t u;
        u.th_id = thread_id;
        u.type = PROCESS;
        //v是鎖原來的線程
        struct pair_t v;
        v.th_id = tg- >lock_list[idx].th_id;
        tg- >lock_list[idx].degress--;
        v.type = PROCESS;
        //刪除邊
        if (verify_edge(u, v)) {
            remove_edge(u, v);
        }
        //設為本線程
        tg- >lock_list[idx].th_id = thread_id;

//        printf("獲得 lock idx:%d n", idx);
    }

    pthread_mutex_unlock_f(&tg- >mutex);
}

4.3 unlock_after

unlock就相當于分手,如果她沒有備胎,那么她就恢復單身(pair置空),如果她有備胎,那就隨她吧~

void unlock_after(unsigned long int thread_id, unsigned long int lock) {
    pthread_mutex_lock_f(&tg- >mutex);

    int idx = search_lock(lock);
    //如果入度為0,說明沒有別的線程指向該鎖,則把這個idx位置置空
    if (tg- >lock_list[idx].degress == 0) {
        tg- >lock_list[idx].th_id = 0;
        tg- >lock_list[idx].lock_id = 0;
    }

    pthread_mutex_unlock_f(&tg- >mutex);
}

5. 死鎖檢測線程的測試

下面我們來測試這個場景。完整代碼在目錄前言中。

圖片

/* ******* ******************Debug 1****************** ******* */
pthread_mutex_t mutex_1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_4 = PTHREAD_MUTEX_INITIALIZER;

void *thread_rountine_1(void *args) {
    pthread_t selfid = pthread_self(); //

    printf("thread_routine 1 : %ld n", selfid);

    pthread_mutex_lock(&mutex_1);
    sleep(1);
    pthread_mutex_lock(&mutex_2);

    pthread_mutex_unlock(&mutex_2);
    pthread_mutex_unlock(&mutex_1);

    return (void *) (0);
}

void *thread_rountine_2(void *args) {
    pthread_t selfid = pthread_self(); //

    printf("thread_routine 2 : %ld n", selfid);

    pthread_mutex_lock(&mutex_2);
    sleep(1);
    pthread_mutex_lock(&mutex_3);

    pthread_mutex_unlock(&mutex_3);
    pthread_mutex_unlock(&mutex_2);

    return (void *) (0);
}

void *thread_rountine_3(void *args) {
    pthread_t selfid = pthread_self(); //

    printf("thread_routine 3 : %ld n", selfid);

    pthread_mutex_lock(&mutex_3);
    sleep(1);
    pthread_mutex_lock(&mutex_4);

    pthread_mutex_unlock(&mutex_4);
    pthread_mutex_unlock(&mutex_3);

    return (void *) (0);
}

void *thread_rountine_4(void *args) {
    pthread_t selfid = pthread_self(); //

    printf("thread_routine 4 : %ld n", selfid);

    pthread_mutex_lock(&mutex_4);
    sleep(1);
    pthread_mutex_lock(&mutex_1);

    pthread_mutex_unlock(&mutex_1);
    pthread_mutex_unlock(&mutex_4);

    return (void *) (0);
}


int main() {
    init_hook();
    start_check();

    printf("start_checkn");

    pthread_t tid1, tid2, tid3, tid4;
    pthread_create(&tid1, NULL, thread_rountine_1, NULL);
    pthread_create(&tid2, NULL, thread_rountine_2, NULL);
    pthread_create(&tid3, NULL, thread_rountine_3, NULL);
    pthread_create(&tid4, NULL, thread_rountine_4, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);

    return 0;
}

圖片

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 死鎖
    +關注

    關注

    0

    文章

    25

    瀏覽量

    8086
  • 函數
    +關注

    關注

    3

    文章

    4346

    瀏覽量

    62968
  • 代碼
    +關注

    關注

    30

    文章

    4825

    瀏覽量

    69043
  • 線程
    +關注

    關注

    0

    文章

    505

    瀏覽量

    19756
收藏 人收藏

    評論

    相關推薦

    DAC7564輸出死鎖是什么原因引起的?

    我用DAC7564輸出,發現DAC7564會不定時的出現某通道輸出死鎖現象(就是輸出固定在某一值,不能再改變了)。斷電重啟后又正常了。 是用IO口模擬的SPI,在發送命令前關中斷,發送完后再開中斷。 請問指點一下,這種情況可能是什么原因引起的?用這個芯片要注意什么
    發表于 02-05 08:44

    STM32應用實例十:簡析STM32 I2C通訊死鎖問題

    時鐘為高,而SDA的電平時鐘為低。如果拔掉對應的設備,SCL的波形則恢復正常。接上設備恢復正常,但運行一會現象依舊。一開始以為是連接的設備有問題,于是換了一臺設備,發現依舊如此。難道真的是I2C出現了死鎖
    發表于 05-15 13:36

    哪些因素影響了FPGA的并行多通道激勵信號產生?

    并行測試的實現途徑分為軟件方式和硬件方式。用軟件方式實現并行測試,關鍵是對測試任務的分解和調度,但可能會產生競爭或者死鎖現象。因此,在測試資源有限并且任務分解和調度算法不成熟的情況下,用軟件實現并行測試會很困難。那么,為什么說對多通道并行激勵信號的需求也是影響并行測試的關
    發表于 08-13 08:08

    FPGA并行多通道信號產生模塊有什么特點?

    并行測試的實現途徑分為軟件方式和硬件方式。用軟件方式實現并行測試,關鍵是對測試任務的分解和調度,但可能會產生競爭或者死鎖現象。因此,在測試資源有限并且任務分解和調度算法不成熟的情況下,用軟件實現并行
    發表于 08-16 06:50

    拔插法與替換法簡析

    1、拔插法:“拔插法”是將插件“拔出”或“插入”來尋找故障的方法。例如,機器出現“死鎖現象,采用這種方法一塊一塊地拔出插件板,若機器恢復正常,說明故障出在該板上。2、替換法:替換法”是采用已確定是
    發表于 09-08 06:43

    請問STM32 F103串口同時收發出現死鎖現象怎么解決?

    請問STM32 F103串口同時收發出現死鎖現象怎么解決?
    發表于 02-18 07:56

    強干擾下數據采集系統穩定通訊解決方案

    本文介紹了RS-232 串行總線及USB 通用串行總線技術,討論了其抗干擾性,分析了串口出現誤碼及USB 接口出現死鎖現象的原因,提出了一種強干擾下數據采集系統穩定通訊的解決方案
    發表于 06-19 11:55 ?17次下載

    DIN中的死鎖避免和死鎖恢復

    DIN中的死鎖避免和死鎖恢復 由于存在占用資源者申請另一個資源的情形,在DIN中由于拓撲結構本身存在環狀路徑,所以
    發表于 02-23 14:47 ?940次閱讀
    DIN中的<b class='flag-5'>死鎖</b>避免和<b class='flag-5'>死鎖</b>恢復

    基于FPGA的并行多通道激勵信號產生模塊

    并行測試的實現途徑分為軟件方式和硬件方式。用軟件方式實現并行測試,關鍵是對測試任務的分解和調度,但可能會產生競爭或者死鎖現象。因此,在測試資源有限并且任務分解和調度算法不成熟的情況下,用軟件實現并行測試會很困難。用硬件方式實現并行測試時,需
    發表于 01-21 00:17 ?1208次閱讀
    基于FPGA的并行多通道激勵信號產生模塊

    i2c總線的起始和終止的條件

    盡量選用帶復位輸人的I2C從器件,從I2C總線死鎖產生的原因可以發現I2C總線死鎖的一個必要條件是主設備復位了而從設備沒有復位。如果從設備選用帶復位輸入的芯片,將主從設備的復位信號連接在一起,當外部產生復位事件時,主從設備同時復位,這樣就不會發生I2C總線
    發表于 12-06 15:48 ?2.9w次閱讀
    i2c總線的起始和終止的條件

    如何解決PIC單片機硬件死鎖的問題

    “CMOS的可控硅效應”而產生死鎖現象, 依我各人的觀點,應與 “CMOS的可控硅效應”無關,但很多大蝦皆認為是“CMOS的可控硅效應”所引起的。
    發表于 02-22 15:23 ?3078次閱讀

    STM32 F103串口同時收發出現死鎖問題解決辦法

    一直使用F4系列,沒有出現此類現象,也可能出現了沒有發現。最近在做和研華工控機通訊時出現串口接收問題。總結如下:1. 使用DMA+空閑中斷未出現串口同時收發死鎖現象,但是由于研華某款工控機在與板子
    發表于 12-24 18:40 ?1次下載
    STM32 F103串口同時收發出現<b class='flag-5'>死鎖</b>問題解決辦法

    為什么說線程是輕量級的概念呢?守護線程是指什么?

    當多個線程同時搶多把鎖的時候就會出現死鎖現象。其實遞歸鎖也不是一個很好地解決方案,死鎖現象的發生不是互斥鎖的原因,而是程序猿/媛的邏輯出現了問題。
    的頭像 發表于 08-19 10:39 ?2116次閱讀
    為什么說線程是輕量級的概念呢?守護線程是指什么?

    Linux內核死鎖lockdep功能

    死鎖是指兩個或多個進程因爭奪資源而造成的互相等待的現象,如進程A需要資源X,進程B需要資源Y,而雙方都掌握對方所需要的資源,且都不釋放,這會導致死鎖。 在內核開發中,時常要考慮并發設計,即使采用正確
    的頭像 發表于 09-27 15:13 ?749次閱讀
    Linux內核<b class='flag-5'>死鎖</b>lockdep功能

    死鎖現象以及原理

    。 init_hook (); start_check (); 1. 死鎖現象以及原理 1.1 復現最簡單的死鎖 線程A占有鎖1,線程B占有鎖2;此時線程A想要獲取鎖2,但是鎖2已經被線程B占有, 此時線程
    的頭像 發表于 11-13 16:30 ?584次閱讀
    <b class='flag-5'>死鎖</b>的<b class='flag-5'>現象</b>以及原理
    盛世国际娱乐城| 百家乐微心打法| 百家乐官网博欲乐城| 星期8百家乐娱乐城| 福布斯百家乐的玩法技巧和规则 | 澳门百家乐游戏说明书| 百家乐好多假网站| 百家乐对冲套红利| 百家乐娱乐平台网| 百家乐官网看盘技巧| 卡迪拉娱乐城开户| 德州扑克与梭哈| 真人娱乐场注册送现金| 有百家乐官网的棋牌游戏| 迪威百家乐官网娱乐平台| 百家乐官网专业赌| 网上百家乐骗局| 威尼斯人娱乐城怎么玩| 济州岛娱乐场小伊| 百家乐官网六亿财富| 太原百家乐官网的玩法技巧和规则| 百家乐国际娱乐城| 大发888使用条款| 开心8百家乐官网现金网| 网上百家乐官网分析软件| 百家乐官网创立几年了| 百家乐怎么稳赢| 大发888 备用6222.co| 网上百家乐官网骗局| 百家乐双龙出海注码法| 永利高| 下载百家乐官网的玩法技巧和规则| 玩百家乐去哪个娱乐城最安全| 百家乐官网巴厘岛平台| 将军百家乐官网的玩法技巧和规则 | 24山方位| 百家乐网页游戏| 百家乐官网翻天快播粤语| 百家乐注册赠分| 大发888代充| 菠菜百家乐官网娱乐城|