<samp id="rdqqw"><output id="rdqqw"></output></samp>

    當前位置:首頁 > 公眾號精選 > 程序員寫個解
    [導讀]在我看來最不值得一提的BUG是那種可以重復復現的,他的穩定復現通常排查起來沒啥技術含量, 早些年我處理一個不值得一提的BUG,BUG也很好復現,難點是復現時間固定在4小時左右,BUG由于文件資源未釋放引起進程訪問文件數目受限而崩潰,早期Android系統用該BUG獲取到root權限, 本文向你分享,如何根據錯誤提示和參考手冊找到故障點,指導新碼農如何正確閱讀Linux幫助手冊(man page), 最后總結我的排查過程給小白一點實用的建議。好下面開始不如步入正題。需要調試的是一個監控程序,代碼非常簡單,2個線程執行不同的任務,每個任務都是間隔15秒執行一次,程序固定在大約4小時后崩潰。代碼簡單到用不著任何同步機制、沒有任何通信,極少的內存訪問,按理來說他就不應該存在BUG,然而還是發生了。

    最不值得一提的BUG

    在我看來最不值得一提的BUG是那種可以重復復現的,他的穩定復現通常排查起來沒啥技術含量, 早些年我處理一個不值得一提的BUG,BUG也很好復現,難點是復現時間固定在4小時左右,BUG由于文件資源未釋放引起進程訪問文件數目受限而崩潰,早期Android系統用該BUG獲取到root權限, 本文向你分享,如何根據錯誤提示和參考手冊找到故障點,指導新碼農如何正確閱讀Linux幫助手冊(man page), 最后總結我的排查過程給小白一點實用的建議。好下面開始不如步入正題。需要調試的是一個監控程序,代碼非常簡單,2個線程執行不同的任務,每個任務都是間隔15秒執行一次,程序固定在大約4小時后崩潰。代碼簡單到用不著任何同步機制、沒有任何通信,極少的內存訪問,按理來說他就不應該存在BUG,然而還是發生了。

    第1個4小時:縮小排查范圍,是什么引起段錯誤

    在源碼若干位置加上打印執行的函數、行號, 打開調試選項重新編譯應用程序,開啟coredump選項,耐心等待4小時后故障復現。gdb打開coredump 確認段錯誤(Segmentation fault),棧溯確認崩潰現場調用棧。段錯誤位于ti_ck_mutil函數第266行之后。
    TickStatusIO():105ti_ck_mutil():266Segmentation fault (core dumped) (gdb) bt#0  0x401b28e0 in vfwprintf () from /lib/libc.so.6#1  0x00009d10 in ti_ck_mutil (cmdstr=0xbebffa4c, len=1) at src/ti.c:268#2  0x00008e2c in TickStatusIO () at src/initgpio.c:106#3  0x00009238 in main (argc=1, argv=0xbebffbf4) at src/initgpio.c:304

    審查ti_ck_mutil函數內226行之后的代碼,結合棧底位置是vfwprintf函數入口,基本可以確定導致崩潰位置是fread函數,fread可能會有什么錯誤呢?
    int ti_ck_mutil(char *cmdstr, int count){ FILE *stream; char strout[256]; int ret, failcount = 0;  for (int i = 0; i < count; i++) { printf("%s()%d\n", __FUNCTION__, __LINE__);//226行 stream = popen(cmdstr, "r");//未檢查文件是否成功 ret = fread(strout, sizeof(char), sizeof(strout), stream); // 228行  strout[ret] = '\0'; pclose(stream); // ... } return failcount;}
    fread輸入參數只有4個,猜測可能存在的失敗原因有3點:
    1、被編譯器優化后strout的緩存不是256

    但后面用的是算數表達式sizeof,就算被優化也不會造成錯誤。
    觀點:暫時不去瞎想。2、fread寫入最后一個字符時溢出。

    strout后第256地址也被填寫了,實際我讀寫的文件不超過64byte,不應該超過256。
    即使第256地址被fread寫了,相當于內存訪問越接。訪問越接發生什么錯誤都不奇怪,輕微越接會影響附近變量的值,比如ret和stream的值改變,大范圍越界破壞調用棧。觀點:猜測fread可能訪問越限,但絕對沒破壞調用棧。若破壞調用棧,那么棧不會是整整齊齊打印4個函數,而是輸出若干問號(“?? ()”),找不到函數名稱標簽。
    #0  0x000028e0 in ?? () #1  0x000038e8 in ?? () #2  0x000048ec in ?? () #3  0x000068e0 in ?? () #4  0x00009d10 in ti_ck_mutil (cmdstr=0xbebffa4c, len=1) at src/ti.c:268#5  0x00008e2c in TickStatusIO () at src/initgpio.c:106#6  0x00009238 in main (argc=1, argv=0xbebffbf4) at src/initgpio.c:304

    3、stream文件描述符無效觀點:有可能,源碼未對popen返回結果做判斷。

    第2個4小時:是內存越界?還是資源不足?

    于是結合猜測2和3,對源碼做2處理修改:1、不向fread傳遞完整內存長度,保證最后一個字符不被fread填寫 2、判斷popen返回值
    stream = popen(cmdstr, "r");ret = fread(strout, sizeof(char), sizeof(strout), stream); 修改后 stream = popen(cmdstr, "r");if (stream == 0) { perror("popen error:");}ret = fread(strout, sizeof(char), sizeof(strout) - 1, stream);
    
    繼續等待4小時,程序依舊崩潰,輸出崩潰前提示執行popen失敗,返回值0,錯誤原因記錄在errno里,errno指示打開太多文件,資源不足。
    	
    popen error:: Too many open files

    機理分析:為什么文件打開太多?

    進一步定位到故障點在popen函數上,問題是:啥叫文件打開太多?查看popen幫助介紹:man popen?;蛟S能給我解釋
    RETURN VALUEThe popen() function returns NULL if the fork(2) or pipe(2) calls fail, or if it cannot allocate memory.
    本質上popen是個“殼",它返回0的原因有兩個:1、它間接調用fork()創建子進程執行腳本,間接調用pipe()創建管道,子進程輸出信息從管道傳遞到父進程。2、沒有足夠的內存分配。從第2點:沒有足夠的內存方向去排查,無非是內存泄漏咯,通常是申請內存有釋放干凈導致。c語言標準內存分配函數有malloc、calloc、realloc、reallocarray,對應的釋放函數只有free。我應該在源碼上搜索,是否所有“分配函數和釋放函數都一一配對”,哦~別忘了,小白可能還不清楚,除了常用的malloc外,還有像mmap這樣的內存分配函數,它有專用的釋放函數munmap。從搜索結果上看,數目是能對得上的,暫且粗略的判定不存內存泄漏。更仔細的排查方向應該是:確定代碼執行流真的執行到釋放函數,而不是單純地看數目是否匹配。



    在繼續閱讀popen的errors段落描述。
    ERRORSThe popen() function does not set errno if memory allocation fails. If the underlying fork(2) or pipe(2) fails, errno is set appropriately. If the type argument is invalid, and this condition is detected, errno is set to EINVAL.
    popen不會因為內存分配失敗而在errno記錄錯誤碼,如果是fork()或pipe()函數執行失敗則在errno設置相應錯誤碼。忙半天忙個寂寞,年輕人,別學會寫一、二、三,就自以為無師自通懂得寫四、寫一萬。讀完man全文再入手好不好!既然errno提示具體錯誤信息,就不可能是內存泄漏,執行失敗原因一定是Too many open files的字面意思。回想以前初學Linux時有個知識點:為了防止某用戶打開過多的文件,系統對進程件訪問數目有限制,默認是1024。2016年4月參加宋寶華的線下培訓,他說Android剛出來時有個提權的方法(root權限):創建1024個無用子進程資源且不釋放,第1025個進程就能得到root權限。命令查看應用程序運行一段時間后,有多少文件描述符號(file descriptor)沒有釋放。果然,每間隔15秒文件描述符就多一個。256分鐘后達到1024個文件描述符,時間上和軟件4小時崩潰很接近。
    watch -n 1 ls -l /proc/PID/fd

    再用之前的篩選方法:排查open和close的函數是一一匹配。發現open關鍵詞篩選出
    6行,close作為關鍵詞篩出5行。opendir沒有對應的close。


    捂臉?。?!“Linux下一切皆是文件”我還沒理解透徹,沒意識到打開目錄(opendir)也是文件資源,應用程序某線程每間隔15秒就訪問一次目錄。man opendir確認closedir是它的配對關閉函數。
    SEE ALSOopen(2), closedir(3), dirfd(3), readdir(3), rewinddir(3), scandir(3), seekdir(3), telldir(3)
    添加上closedir后故障得以修復。

    順帶提一下

    貼圖用的搜索工具不是grep而是我自己寫的腳本jgrep,它的用法和grep完全一樣,輸入前面的數字能打開對于文件所在行,對于搜索源碼、系統配置文件檢索、跳轉特別適用。如果你對jgrep感興趣的話,在我的公眾號“程序員寫個解”發送 “20220411” 可獲取。




    總結建議

    BUG成功得以修復,它本是不應該犯的錯誤,在這里我給自己和讀者建議:1、以后使用不熟悉的API,首先查閱他的幫助手冊2、對于內存分配函數有相互獨立的API,比如malloc對應free、mmap對應munmap。跟著手冊建議的API去掉釋放資源,避免不可預知的故障發生。最后,如果你覺得文章對你有所幫助,有啟發作用。歡迎點擊,把今天的內容分享給你的好友,和他一起討論學習。
    本站聲明: 本文章由作者或相關機構授權發布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
    換一批
    延伸閱讀

    作者:vivo互聯網服務器團隊-ZhangZhenglin一、簡介RocketMQ是阿里巴巴開源的分布式消息中間件,它借鑒了Kafka實現,支持消息訂閱與發布、順序消息、事務消息、定時消息、消息回溯、死信隊列等功能。Ro...

    關鍵字: 源碼 存儲模塊 ck

    來源:https://www.cnblogs.com/deng-cc/p/6927447.html最近正好也沒什么可忙的,就回過頭來鼓搗過去的知識點,到Servlet部分時,以前學習的時候硬是把從上到下的繼承關系和接口實...

    關鍵字: IDE 源碼 Diagram

    一、前言老周這里編譯Kafka的版本是2.7,為啥采用這個版本來搭建源碼的閱讀環境呢?因為該版本相對來說比較新。而我為啥不用2.7后的版本呢?比如2.8,這是因為去掉了ZooKeeper,還不太穩定,生產環境也不太建議使...

    關鍵字: 源碼 編譯

    國慶的時候閑來無事,就隨手寫了一點之前說的比賽的代碼,目標就是保住前100混個大賽的文化衫就行了?,F在還混在前50的隊伍里面,穩的一比。其實我覺得大家做柔性負載均衡那題的思路其實都不會差太多,就看誰能把關鍵的信息收集起來...

    關鍵字: 源碼

    點擊上方“小麥大叔”,選擇“置頂/星標公眾號”福利干貨,第一時間送達大家好,我是小麥,以前用單片機做用戶交互的菜單的時候,都比較痛苦,如何寫一個復用性高,方便維護,可擴展性高的GUI框架呢?當然可以自己動手寫一個,這個過...

    關鍵字: 單片機 源碼

    知道有多少人折騰過液晶顯示的菜單,我覺得很多人都應該搞過,我還記得以前大學參加電子設計競賽獲獎的作品,我就用到了一個12864,里面有菜單功能。以前可能覺得菜單高大上,其實并不是想象中的復雜,本文為大家分享一個用單色屏做...

    關鍵字: 源碼

    知道有多少人折騰過液晶顯示的菜單,我覺得很多人都應該搞過,我還記得以前大學參加電子設計競賽獲獎的作品,我就用到了一個12864,里面有菜單功能。以前可能覺得菜單高大上,其實并不是想象中的復雜,本文為大家分享一個用單色屏做...

    關鍵字: 源碼

    作者:vivo互聯網服務器團隊-YeWenhao一、RocketMQ架構簡介1.1邏輯部署圖(圖片來自網絡)1.2核心組件說明通過上圖可以看到,RocketMQ的核心組件主要包括4個,分別是NameServer、Brok...

    關鍵字: 源碼 ck

    公眾號「程序員內點事」?對于Nacos大家應該都不太陌生,出身阿里名聲在外,能做動態服務發現、配置管理,非常好用的一個工具。然而這樣的技術用的人越多面試被問的概率也就越大,如果只停留在使用層面,那面試可能要吃大虧。比如我...

    關鍵字: 模型 源碼 os

    來源:https://www.aneasystone.com/archives/2018/06/insert-locks-via-mysql-source-code.html在之前的博客中,我寫了一系列的文章,比較系統的...

    關鍵字: 源碼

    編輯精選

    技術子站

    關閉
    国内精品久久久久久99