一次程序hang住問題分析。。。

2022-02-02 12:57:01 字數 2551 閱讀 6456

這兩天有同學使用資料校驗工具時發現程序hang住了,也不知道什麼原因,我簡單看了看程序堆疊,問題雖然很簡單,但能導致程式hang住,也一定不是小問題。簡單說明下程式元件的結構,程式由兩部分構成,dbchk和dbchk_inner,dbchk採用python**實現,dbchk_inner採用c語言實現。dbchk負責併發控制,dbchk_inner則負責具體的校驗任務。使用者通過執行dbchk命令即可達到校驗的目的。程序關係如下:

$ pstree 18649

dbchk─┬─sh───dbchk_inner───2*

└─回到問題本身,我用測試用例復現了hang住了場景,檢視了dbchk和dbchk_inner的堆疊資訊,資訊如下:

dbchk程序18649堆疊資訊:

$ pstack 18649

thread 2 (thread 0x7f4343fff700 (lwp 18658)):

#0 0x000000346f80f09d in waitpid () from /lib64/libpthread.so.0

#1 0x000000347190ff8a in ?? () from /usr/lib64/libpython2.6.so.1.0

#2 0x00000034718de706 in pyeval_evalframeex () from /usr/lib64/libpython2.6.so.1.0

#3 0x00000034718e0797 in pyeval_evalcodeex () from /usr/lib64/libpython2.6.so.1.0

dbchk_inner程序18660堆疊資訊:

pstack 18660

#0 0x000000346f4da3dd in write () from /lib64/libc.so.6

#1 0x000000346f470fd3 in _io_new_file_write () from /lib64/libc.so.6

#2 0x000000346f470e9a in _io_new_file_xsputn () from /lib64/libc.so.6

#3 0x000000346f46705d in fwrite () from /lib64/libc.so.6

#4 0x00000000004136f0 in scanner::run(unsigned int) ()

可以看到父程序dbchk在卡在waitpid()函式,這個容易理解,它應該在等待子程序dbchk_inner結束;再看子程序dbchk_inner,dbchk_inner卡在fwrite()函式,這個就有點奇怪了,為啥寫會被阻塞呢?首先想到的是磁碟空間不夠了?看了下磁碟空間還有很大的剩餘,那還有什麼可能導致write卡住,還有一種可能就是緩衝區滿了,寫不下去。

基於這個思考,回頭看看dbchk的**

pio = subprocess.popen(command, shell=true, stdout=subprocess.pipe, stderr=subprocess.pipe).wait()

可以看到程式裡使用了popen的wait函式,這可以解釋父程序為啥會卡住,因為子程序沒有執行完;注意popen的引數,將stdout和stderr輸出重定向到了subprocess.pipe,這個值表示父子程序之間的管道。那麼子程序寫緩衝區卡住,應該就是因為pipe的緩衝區滿了。為啥會滿呢,一是產生的資料太多;另一方面是沒有程序去緩衝區去取資料,導致緩衝區只進不出。pipe緩衝區預設值大小4096個位元組,這個可以通過ulimit -a得到,8*512=4096位元組,並且這個值不可以修改的,因為值是定義在linux的標頭檔案裡面,除非你重新編譯linux核心。

$ ulimit -a

core file size (blocks, -c) 0

……pipe size (512 bytes, -p) 8

好了問題找到了,pipe緩衝區滿是罪歸禍首,如何解這個問題?

1.不將stdout和stderr重定向管道,直接輸出

2.程式控制輸出到管道資料的大小

管道在程序間通訊(ipc)使用很廣泛,shell命令就使用的很廣泛。比如:

ps –aux | grep mysqld

1.      管道是半雙工的,資料只能單向流動,ps命令的輸出是grep的輸出

2.      只能用於父子程序或兄弟程序通訊,這裡可以認為ps和grep命令都是shell(bash/pdksh/ash/dash)命令的子程序,兩者是兄弟關係。

3.      管道相對於管道兩端的程序而言就是乙個檔案,並且只存在於記憶體中。

4.      寫入端不斷往管道寫,並且每次寫到管道末尾;讀取端則不斷從管道讀,每次從頭部讀取。

到這裡大家可能會有乙個疑問,管道兩端的程序,寫入程序不斷的寫,讀取程序不斷的讀,那麼什麼時候結束呢?比如我們剛剛這個命令很快就結束了,它的原理是怎麼樣的呢?對於管道,這裡有兩個基本原則:

1.當讀乙個寫端已經關閉的管道時,在所有資料被讀取後,read返回0,以指示達到檔案結束處。

2.當寫乙個讀端已經關閉的管道時,會產生sigpipe資訊。

結合這個例子,當ps寫管道結束後,就會自動關閉,此時grep程序read就會返回0,然後自動結束。

《unix環境高階程式設計》

一次程序hang住問題分析。。。

這兩天有同學使用資料校驗工具時發現程序hang住了,也不知道什麼原因,我簡單看了看程序堆疊,問題雖然很簡單,但能導致程式hang住,也一定不是小問題。簡單說明下程式元件的結構,程式由兩部分構成,dbchk和dbchk inner,dbchk採用python 實現,dbchk inner採用c語言實現...

feof多讀一次問題分析

在讀完檔案的最後乙個字元後,fp flag仍然沒有被置為 ioeof,因而feof 仍然沒有探測到檔案結尾。直到再次呼叫fgetc 執行讀操作,feof 才能探測到檔案結尾。這樣就多執行了一次。對於feof 這個函式,它是先讀再判斷是否到檔案尾,也就是說在它之前一定要讀一次才能做出判斷。而我們經常這...

第一次作業 深入原始碼分析程序模型

程序 這是對正在執行程式的乙個抽象。作業系統的其它所有內容都是圍繞著程序的概念展開的。乙個程序就是乙個正在執行程式的例項。1.程序數 在sched.h中,有如下語句 1 define nr tasks 64 該語句定義了系統中的最多工 程序 數,即 在同一瞬間,系統中最多可有64個程序。2.程序識別...