摘要:連續多次查看這個進程的函數調用關係堆棧,死鎖線程將一直處於等鎖狀態,對比多次的函數調用堆棧輸出結果,確定哪兩個線程(或者幾個線程)一直沒有變化且一直處於等鎖的狀態,給出運行結果截圖,在圖中標出死鎖出現的地方,並分析爲什麼會出現死鎖代碼設計:假設只有一個生產者,卻有一個消費者,生產者一次生產一個資源,消費者一次消耗一個資源,按照基本原理應該是先申請資源,進而互斥鎖上鎖,若申請失敗,就不上鎖,等待申請成功,再上鎖。int init3 = pthread_mutex_init(&mutex, NULL)。

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><div>
<h2>
    <span>一. 實驗目的及實驗環境</span>
</h2>
<h3>
    <span>1.實驗目的</span>
</h3>
通過觀察、分析實驗現象,深入理解產生死鎖的原因,學會分析死鎖的方法, 並利用 pstack、 gdb 或 core 文件分析( valgrind (DRD+Helgrind) 可選 )其中的一 種方法來分析死鎖。
<h3>
    <span>2.實驗環境    </span>
</h3>

    <span>(1)硬件</span>

CPU:Intel i5
內存:16G
顯示器:NVIDIA 1050Ti
硬盤空間:1T    

    <span>(2)軟件  </span>

虛擬機名稱及版本:VMware
操作系統名稱及版本:Ubuntu16.04
編譯器:gedit
<h2>
    <span>二. 實驗內容</span>
</h2>
<h3>
    <span>1、實驗前準備工作  </span>
</h3>
仔細閱讀參考資料 Linux 死鎖現象及分析方法,瞭解對死鎖現象進行分析的各種工具,選擇其中一種對死鎖現象進行分析。
<h3>
    <span>2、實驗內容</span>
</h3>
1)準備好生產者-消費者問題,或者哲學家就餐問題產生死鎖的代碼。
2)編譯程序後,注意加調試選項-g,先預計一下這個程序的運行結果,運行程序,若程序沒有響應,按 ctrl+c 中斷程序運行,然後再重新運行,如此反覆若干次,記錄下每次的運行結果。若產生了死鎖,通過工具對死鎖進行分析。
<h2>
    <span>三、實驗結果分析</span>
</h2>
連續多次查看這個進程的函數調用關係堆棧,死鎖線程將一直處於等鎖狀態,對比多次的函數調用堆棧輸出結果,確定哪兩個線程(或者幾個線程)一直沒有變化且一直處於等鎖的狀態,給出運行結果截圖,在圖中標出死鎖出現的地方,並分析爲什麼會出現死鎖代碼設計:假設只有一個生產者,卻有一個消費者,生產者一次生產一個資源,消費者一次消耗一個資源,按照基本原理應該是先申請資源,進而互斥鎖上鎖,若申請失敗,就不上鎖,等待申請成功,再上鎖。爲了產生死鎖條件修改順序:先互斥鎖上鎖,然後再進行資源申請。這樣有可能出現生產者未來得及生產資源,消費者就進行申請,但先上鎖後申請,所以未申請到,不會解鎖,因爲互斥鎖未解鎖,生產者無法生產。舉個簡單的例子(和我組成員劉傳璽一同商討得出):
假設有一個筐子,甲做饅頭,乙喫饅頭,合理的情況應該是乙看一眼筐裏有沒有饅頭,若有,則伸手去取,若沒有,則等甲放進去,再取;相對應,如果做甲看見乙在取饅頭,此時筐子被佔用了,甲暫時還不能放饅頭進去,等乙取完了, 甲才放新饅頭。就這樣有條不紊一直運行。但是現在情況變了,乙不管三七二十一伸手就拿,要是拿到了還好,就喫了,要是手快了,饅頭還沒做好,他伸手取一抓,沒抓找,手就放在筐子裏等,甲一看手在筐裏放着,我饅頭也放不進去啊,那就等他把手拿出來再放進去... ...一個在等饅頭來,一個在等手出去:死鎖!

    <span>發生死鎖,無資源,卻申請資源</span>
    <span>:     </span>

<p style="text-align:center">
    <img src="https://img2.tuicool.com/UBZnmuU.png!web" class="alignCenter" referrerpolicy="no-referrer"/>
</p>

    <span>進行檢查</span>
    <span>:     </span>


    <img src="https://img2.tuicool.com/r2iIjqf.png!web" class="alignCenter" referrerpolicy="no-referrer"/>
    <span>鎖定錯誤位置 :</span>

<p style="text-align:center">
    <img src="https://img2.tuicool.com/AZJRBfe.png!web" class="alignCenter" referrerpolicy="no-referrer"/>
</p>
互斥鎖先鎖定後申請資源,順序出錯,可能會導致死鎖。發現錯誤,解決錯誤:
<p style="text-align:center">
    <img src="https://img0.tuicool.com/3aa2I3r.png!web" class="alignCenter" referrerpolicy="no-referrer"/>
</p>
<h2>
    <span>四、總結</span>
</h2>
平時閱讀代碼,覺得一切順理成章,非常自然,從未思考爲何要這樣做。通過本次實驗,老師逆向思維,讓我們寫出死鎖!所有代碼都在避開死鎖,老師讓我們寫出死鎖,無從下手,毫無頭緒,實在讓人頭疼。查閱資料,反覆理解運行順序: 申請,上鎖,釋放,來來回回,費九牛二虎之力才寫出死鎖。回頭觀望, 瞬間恍然大悟,明白老師了良苦用心,躲避錯誤人人都會,但如果我能從無錯中犯錯,也就是說我理解了整個運行結構,操作流程之後,才能知道在何處會犯錯, 能犯錯,通過犯錯讓我們更深刻的體會錯誤,理解錯誤。從而根本的明白錯誤發生的原因以及修改的方式。不得不說實在高明。同時我也感受到了 Linux 代碼的嚴謹,僅僅是兩行代碼順序調換,就發生了 意想不到的錯誤,若在大工程中犯錯,可能會帶來毀滅性的後果。讓我在感嘆代碼嚴謹的同時,也讓我明白了不可以抱有僥倖心理,只有錯和不錯,沒有可能一說!可能有錯那就是錯誤,100%正確纔是真正的正確,嚴謹認真、高效簡潔是編寫代碼要有的思維風範。
<h2>
    <span>五.附錄:源代碼(電子版)</span>
</h2>
<pre class="prettyprint"><span><span>#<span>include</span> <span>&lt;stdio.h&gt;</span></span></span>
#include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h>
#define M 1 #define P(x) sem_wait(&x) #define V(x) sem_post(&x)
int in = 0; int out = 0; int buff[M] = {0};
sem_t sem_dr; sem_t sem_co; pthread_mutex_t mutex;
void print() { static int number = 0; int i; printf("(%2d)\t",number); for(i = 0; i < M; i++) printf("%d ", buff[i]); number++; printf("\n"); }
void *producer() { for(;;) { sleep(1); P(sem_dr); pthread_mutex_lock(&mutex); in = in % M; printf("(+)produce a product. buffer:"); buff[in] = 1; print(); ++in; pthread_mutex_unlock(&mutex); V(sem_co); } }
void *consumer() { for(;;) { sleep(1); pthread_mutex_lock(&mutex); P(sem_co); out = out % M; printf("(-)consume a product. buffer:"); buff[out] = 0; print(); ++out; pthread_mutex_unlock(&mutex); V(sem_dr); } }
void sem_mutex_init() { int init1 = sem_init(&sem_dr, 0, M); int init2 = sem_init(&sem_co, 0, 0); if( (init1 != 0) && (init2 != 0)) { printf("sem init failed \n"); exit(1); } int init3 = pthread_mutex_init(&mutex, NULL); if(init3 != 0) { printf("mutex init failed \n"); exit(1); } }
int main() { pthread_t id1; pthread_t id2; int i; int ret; sem_mutex_init(); /create the producer thread/ ret = pthread_create(&id1, NULL, producer, NULL); if(ret != 0) { printf("producer creation failed \n"); exit(1); } ret = pthread_create(&id2, NULL, consumer, NULL); if(ret != 0) { printf("consumer creation failed \n"); exit(1); } pthread_join(id1,NULL); pthread_join(id2,NULL); exit(0); }
相關文章