久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux應(yīng)用程序錯(cuò)誤使用pthread

 slimfeng 2019-09-05

本文分析在Linux應(yīng)用程序中錯(cuò)誤使用pthread_mutex鎖時(shí)會(huì)概率性觸發(fā)SIG_ABRT信號(hào)而導(dǎo)致程序崩潰(庫(kù)打印輸出 :Assertion `mutex->__data.__owner == 0' failed)的原因。

程序環(huán)境如下:(1)Glibc-2.15 (2)Linux-4.1.12 (3)樹莓派1b

首先給出出錯(cuò)的示例程序:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include "pthread.h"
  4. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  5. void * process(void * arg)
  6. {
  7. fprintf(stderr, "Starting process %s\n", (char *) arg);
  8. while (1) {
  9. /* 加鎖等待某些資源 */
  10. pthread_mutex_lock(&lock);
  11. fprintf(stderr, "Process %s lock mutex\n", (char *) arg);
  12. /* 加鎖成功表示資源就緒 */
  13. usleep(1000);
  14. /* do something */
  15. }
  16. return NULL;
  17. }
  18. int main(void)
  19. {
  20. pthread_t th_a, th_b;
  21. int ret = 0;
  22. ret = pthread_create(&th_a, NULL, process, "a");
  23. if (ret != 0) fprintf(stderr, "create a failed %d\n", ret);
  24. ret = pthread_create(&th_b, NULL, process, "b");
  25. if (ret != 0) fprintf(stderr, "create b failed %d\n", ret);
  26. while (1) {
  27. /* 等待并檢測(cè)某些資源就緒 */
  28. /* something */
  29. /* 解鎖告知線程資源就緒 */
  30. pthread_mutex_unlock(&lock);
  31. fprintf(stderr, "Main Process unlock mutex\n");
  32. }
  33. return 0;
  34. }

本示例程序中,main函數(shù)首先創(chuàng)建兩個(gè)線程,,然后主線程等待某些資源就緒(偽代碼,,程序中未體現(xiàn)),待就緒后解鎖mutex lock以告知子線程可以執(zhí)行相應(yīng)的處理(在解鎖后打印輸出解鎖成功),,不斷循環(huán),;創(chuàng)建出的兩個(gè)線程均調(diào)用process函數(shù),該函數(shù)會(huì)嘗試加鎖mutex lock,,加鎖成功則表示資源就緒可以處理(打印輸出加鎖成功),,否則在鎖上等待,亦往復(fù)循環(huán),。本程序中對(duì)mutex鎖的用法特殊,,并不對(duì)臨界資源進(jìn)行保護(hù),而是作為線程間”生產(chǎn)---消費(fèi)“同步功能的一個(gè)簡(jiǎn)化示例,,加鎖以等待資源就緒,,解鎖以通知資源就緒,加鎖和解鎖的操作分別在不同的線程中執(zhí)行,。

運(yùn)行該程序后不到10s時(shí)間程序就會(huì)出錯(cuò)退出,,并且觸發(fā)SIG_ABRT信號(hào),終端打印輸出如下:

......

Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Process b lock mutex
Process a lock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Process b lock mutex
pthread_test: pthread_mutex_lock.c:62: __pthread_mutex_lock: Assertion `mutex->__data.__owner == 0' failed.
Aborted

程序在Glibc庫(kù)中的pthread_mutex_lock.c的第62行__pthread_mutex_unlock()函數(shù)中出錯(cuò),,程序ABRT退出,。

下面先來(lái)分析對(duì)應(yīng)的源碼,首先是加鎖流程:


加鎖函數(shù)源碼:

  1. int
  2. __pthread_mutex_lock (mutex)
  3. pthread_mutex_t *mutex;
  4. {
  5. assert (sizeof (mutex->__size) >= sizeof (mutex->__data));
  6. unsigned int type = PTHREAD_MUTEX_TYPE (mutex);
  7. if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))
  8. return __pthread_mutex_lock_full (mutex);
  9. pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
  10. if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)
  11. == PTHREAD_MUTEX_TIMED_NP) //1---判斷鎖類型
  12. {
  13. simple:
  14. /* Normal mutex. */
  15. LLL_MUTEX_LOCK (mutex); //2---加鎖(原子操作)
  16. assert (mutex->__data.__owner == 0); //3---Owner判斷
  17. }
  18. ...
  19. /* Record the ownership. */
  20. mutex->__data.__owner = id; //4---Owner賦值
  21. #ifndef NO_INCR
  22. ++mutex->__data.__nusers;
  23. #endif
  24. return 0;
  25. }
加鎖函數(shù)的主要4步操作已經(jīng)列出,,首先會(huì)判斷鎖的類型,這里僅對(duì)PTHREAD_MUTEX_TIMED_NP類型的鎖做出分析,,該該類型的鎖為默認(rèn)的鎖類型,,當(dāng)一個(gè)線程加鎖后其余請(qǐng)求鎖的線程會(huì)排入一個(gè)等待隊(duì)列,并在鎖解鎖后按優(yōu)先級(jí)獲得鎖,。然后程序調(diào)用LLT_MUTEX_LOCK()宏執(zhí)行底層加鎖動(dòng)作,,這個(gè)加鎖流程是原子的且不同的架構(gòu)實(shí)現(xiàn)并不相同,然后會(huì)判斷是否已經(jīng)有線程獲取了該鎖(因?yàn)镻THREAD_MUTEX_TIMED_NP類型的鎖是不允許嵌套加鎖的),,若已經(jīng)有線程獲取了鎖則出錯(cuò)退出(示例程序中就是在此出錯(cuò)的),,在函數(shù)的最后會(huì)把當(dāng)前獲得鎖的線程號(hào)賦給__owner字段(線程與鎖綁定)就結(jié)束了,此時(shí)當(dāng)前線程進(jìn)入臨界區(qū),,其他對(duì)鎖請(qǐng)求的線程將阻塞,。下面來(lái)看一下解鎖流程:

解鎖函數(shù)源碼:

  1. int
  2. internal_function attribute_hidden
  3. __pthread_mutex_unlock_usercnt (mutex, decr)
  4. pthread_mutex_t *mutex;
  5. int decr;
  6. {
  7. int type = PTHREAD_MUTEX_TYPE (mutex);
  8. if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))
  9. return __pthread_mutex_unlock_full (mutex, decr);
  10. if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP) //1---判斷鎖類型
  11. == PTHREAD_MUTEX_TIMED_NP)
  12. {
  13. /* Always reset the owner field. */
  14. normal:
  15. mutex->__data.__owner = 0; //2---Owner解除
  16. if (decr)
  17. /* One less user. */
  18. --mutex->__data.__nusers;
  19. /* Unlock. */
  20. lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex)); //3---原子解鎖
  21. return 0;
  22. }
  23. ...
  24. }
解鎖函數(shù)的3步主要操作如上,首先依舊是判斷鎖類型,,然后解除鎖和線程的綁定關(guān)系,,最后就調(diào)用lll_unlock()函數(shù)原子的解鎖,此時(shí)若有加鎖線程需要獲取鎖,,相應(yīng)線程會(huì)從

LLT_MUTEX_LOCK()函數(shù)返回繼續(xù)執(zhí)行,。

以上就是調(diào)用pthread mutex函數(shù)加解鎖函數(shù)的主要流程,,其中需要關(guān)注的一點(diǎn)就是這兩個(gè)函數(shù)的執(zhí)行并不是原子的,是可能存在上下文切換動(dòng)作的,。在通常的用法中,,我們加鎖操作一般都是為了保護(hù)臨界資源不被重入改寫,一半都是嚴(yán)格按照“加鎖-->寫入/讀取臨界資源-->解鎖”的流程執(zhí)行(由加鎖的線程負(fù)責(zé)解鎖),,而從前文中分析的__pthread_mutex_lock()和__pthread_mutex_unlock_usercnt()函數(shù)中也可以看到,,只有在原子加鎖期間才會(huì)改變這__owner值(該值也可認(rèn)為是臨界資源的一部分而被保護(hù)起來(lái)了),因此是不可能出現(xiàn)加鎖已經(jīng)加鎖的線程的,,所以也不會(huì)調(diào)用assert()函數(shù)而退出程序的,。

但是本程序中對(duì)鎖的用法顯然并不這么“一般”,而是作為一種線程間的同步功能使用,。其中主進(jìn)程中不停的解鎖,,即是線程A和B沒(méi)有加鎖也同樣如此,而線程A和B會(huì)競(jìng)爭(zhēng)的每隔一定時(shí)間去加鎖,,那么就有可能出現(xiàn)如下圖中所示的一種情況:1,、


該圖中主進(jìn)程待資源就緒后正在解鎖一個(gè)未被加鎖的mutex_lock時(shí)發(fā)成了線程切換,線程A打斷解鎖流程完成了一整個(gè)加鎖的流程,,隨后線程又且換回了主進(jìn)程繼續(xù)執(zhí)行真正的解鎖操作,,這樣線程A所加的鎖就被莫名其妙的解掉了(關(guān)鍵的一點(diǎn)),此時(shí)若線程B在等待該鎖,,則會(huì)進(jìn)入到加鎖流程,,從而在加鎖成功后崩潰在這個(gè)__owner判斷上。其實(shí)該程序出錯(cuò)的主要原因即是解了并未加鎖的mutex_lock,,如若主進(jìn)程解得鎖是已經(jīng)上了鎖的,,則線程A是沒(méi)有機(jī)會(huì)加鎖的,主進(jìn)程會(huì)原子的完成整個(gè)mutex_unlock動(dòng)作,。

另外,,其實(shí)可以適當(dāng)?shù)恼{(diào)整程序再來(lái)看一下另外一種可能的情形(兩個(gè)執(zhí)行流),同樣是“線程間同步”用法:2,、


這種情況就是在資源就緒較慢且資源處理較快的情況容易出現(xiàn)崩潰,,同樣是概率性出現(xiàn)的。最后來(lái)看第三種可能的情況:3,、


這種情況崩潰出現(xiàn)在線程A加鎖的過(guò)程中被主進(jìn)程解鎖,,然后線程A或其他線程又一次加鎖的時(shí)候。其實(shí)不論上述哪一種同步的情況,,其出錯(cuò)的原因有兩點(diǎn):(1)解了未被上鎖的鎖,;(2)A線程加的鎖由其他線程去解,進(jìn)一步分析就是沒(méi)有嚴(yán)格按照“加鎖-->解鎖”的流程使用mutex鎖。

最后對(duì)于以上這種“線程間同步”的使用方法可以使用條件變量或者是信號(hào)量實(shí)現(xiàn)而不要使用mutex鎖,,mutex鎖一般被用在保護(hù)線程間臨界資源的情況下,。

總結(jié):

1、不要去解鎖一個(gè)未被加鎖的mutex鎖,;

2,、不要一個(gè)線程中加鎖而在另一個(gè)線程中解鎖;

3,、使用mutex鎖用于保護(hù)臨界資源,,嚴(yán)格按照“加鎖-->寫入/讀取臨界資源-->解鎖”的流程執(zhí)行,對(duì)于線程間同步的需求使用條件變量或信號(hào)量實(shí)現(xiàn),。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多