容易忽略的expect指令碼問題,暗藏的殭屍程序,wait命令不要漏掉
阿新 • • 發佈:2018-12-14
問題描述
前幾天有個小需求,用到expect指令碼去迴圈的傳送一些資料,主要問題程式碼如下:
#! /usr/bin/expect while {true} { set timeout 60 spawn telnet ip port expect "]'."; send "***********一些資料***********\r" expect "*********一些回覆************"; send "exit\r" expect "Connection closeed by foreign host."; expect eof sleep 10 }
單單看到這段程式碼,並沒有發現什麼問題,但是執行幾個小時之後,收到一個錯誤:
buffer overflow detected *** : /usr/bin/expect terminated
========Backtrace: ========
... ...
記憶體越界?開始排查原因,雖然走了一些彎路,不過終於還是找到了答案:
首先,我們檢視該程序的pid,然後進一步通過pid去程序fd資料夾下看看
[root]# ps -ef|grep expect.sh root 17761 30111 ..... [root]# cd /proc/17761/fd/ [root]# ls -a ...
可以看到該程序已經產生了非常多的子程序控制代碼,趕緊ps -ef|grep 17761
看一下:
[root]# ps -ef|grep 17761 root 23424 17761 ... [telnet] <defunct> root 23426 17761 ... [telnet] <defunct> root 23431 17761 ... [telnet] <defunct> root 23434 17761 ... [telnet] <defunct> root 23438 17761 ... [telnet] <defunct> root 23439 17761 ... [telnet] <defunct> root 23455 17761 ... [telnet] <defunct> ...
原來這個指令碼產生了大量的telnet殭屍程序,導致控制代碼都用完了,但看上面的指令碼,每次telnet都退出了,也都expect eof
結束了程序,怎麼會變成殭屍程序呢?
原因分析
很多人expect指令碼用的不多,基本都是參照網上的例子來完成自己的需求,然後網上的部落格大多都是轉載來轉載去,這導致有些細節問題不常被提及。
首先spawn
會開啟一個子程序(spawn_id)去執行命令,expect eof
用於等待程序結束,另外有一個close
命令用於直接結束子程序(根據spawn_id來定位子程序)。問題在於eof 和 close
都只是殺死子程序,但子程序變為殭屍程序依然存在程序列表中,殭屍程序會佔用控制代碼,但是控制代碼是有限的,大量僵死程序的產生,導致整個指令碼程序無法繼續執行,報錯退出。
網上的例子大多是流程式的指令碼,不是本文這種迴圈執行的,所以等待eof
後,主程序退出,將殭屍程序也回收了,因此不會有任何問題。
不退出主程序,還要及時回收殭屍子程序,很多語言都內建了相關的方法,expect指令碼也不例外,wait
就是負責給子程序收屍的。所以文章開頭的指令碼應該加上wait
來及時回收telnet
殭屍程序。
#! /usr/bin/expect
while {true} {
set timeout 60
spawn telnet ip port
expect "]'.";
send "***********一些資料***********\r"
expect "*********一些回覆************";
send "exit\r"
expect "Connection closeed by foreign host.";
expect eof <---------此句可以換成 close,因為上面已經主動關閉了telnet
wait <----------------wait不可缺少
sleep 10
}
最後
程式碼不在乎簡單複雜,很多東西雖然可以省略,但能做到步步為營也不失是一種優秀的品質。