Cron連線正常工作5次後異常原因分析
目錄
如果一個shell命令的“$?”值為141,則它是收到了SIGPIPE訊號。一些shell指令碼中的sleep或ps、wc等命令無效,也可能是本文描述的原因,解決辦法是忽略掉SIGPIPE,指令碼中可以加句:trap "" SIGPIPE。
問題描述
在Crontab中僅配置如下一條命令(為簡化問題的描述和定位,剔除所有其它的):
*/1 * * * * echo
也就是每分鐘執行一下“echo hello >> /tmp/hello.txt”。
通過觀察發現:
每次重啟cron程序後,都只能連續正常工作5次,也就是可以看到“/tmp/hello.txt”新增5行“hello”。
分析定位
藉助strace工具,cron程序仍然在工作,只是發生了異常:
[pid 12497] select(5, NULL, [4], NULL, {0, 10000}) = 1 (out [4], left {0, 10000})
[pid 12497] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 12497 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
發生了SIGPIPE,導致了子程序退出。翻閱cron的原始碼,確認cron沒有呼叫“signal(SIGPIPE, SIG_IGN);”來忽略SIGPIPE,因此當收到SIGPIPE訊號時,程序必然退出。
進一步追蹤發現,PIPE連線的是“/usr/local/sa/agent/log/sap1012.sock”,另一端則是sap1012:
# lsof /usr/local/sa/agent/log/sap1012.sock
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
sap1012 15415 root 5u unix 0xd940de40 3985983520 /usr/local/sa/agent/log/sap1012.sock
sap1012 15415 root 11u unix 0xe22fa3c0 3970596394 /usr/local/sa/agent/log/sap1012.sock
# ls -l /proc/15415
lrwxrwxrwx 1 root root 0 Apr 17 12:43 exe -> /usr/local/sa/agent/plugins/sap1012-v5.1.4
使用valgrind可以看到對PIPE的寫(send)是在“/lib/libsandbox_web_ips.so”中進行的,利用ldd可以發現這個共享庫在編譯期間就被連結進了cron程式檔案中:
# ldd /usr/sbin/cron
linux-gate.so.1 => (0xffffe000)
/lib/libbash_strip_env.so (0xb7f8c000)
/lib/libsandbox_web_ips.so (0xb7f85000)
libpam.so.0 => /lib/libpam.so.0 (0xb7f70000)
libpam_misc.so.0 => /lib/libpam_misc.so.0 (0xb7f6d000)
libc.so.6 => /lib/libc.so.6 (0xb7e4b000)
libdl.so.2 => /lib/libdl.so.2 (0xb7e47000)
libpthread.so.0 => /lib/libpthread.so.0 (0xb7e32000)
libaudit.so.0 => /lib/libaudit.so.0 (0xb7e1f000)
/lib/ld-linux.so.2 (0xb7f90000)
在Google和Baidu中找不到任何有關“libsandbox_web_ips.so”的蹤影,猜測是有可能是私有的。Cron的Makefile本身並沒有包含libsandbox_web_ips.so”和“/lib/libbash_strip_env.so”,如:
cc -o cron cron.o database.o user.o entry.o job.o do_command.o misc.o env.o popen.o compat.o
經實踐確認,編譯生成可執行檔案後,可執行檔案都會依賴“libsandbox_web_ips.so”和“/lib/libbash_strip_env.so”兩個共享庫,而且它們可能會在被載入時自動執行,但因為符號被stripped掉了,看不到細節。
終於在/etc/ld.so.preload中找到了兩者:
# cat /etc/ld.so.preload
/lib/libbash_strip_env.so
/lib/libsandbox_web_ips.so
它們兩個如同木馬,會伴隨任一程式一起執行,但libsandbox_web_ips.so可能存在問題,導致cron執行異常。
解決方法
kill掉sap1012程序,然後重啟cron程序,這樣cron就可以正常工作了。cron必須在sap1012之前啟動,以規避問題。通過strace會發現變成如下:
[pid 11704] socket(PF_FILE, SOCK_STREAM, 0) = 3
[pid 11704] fcntl64(3, F_GETFL) = 0x2 (flags O_RDWR)
[pid 11704] fcntl64(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
[pid 11704] connect(3, {sa_family=AF_FILE, path="/usr/local/sa/agent/log/sap1012.sock"}, 110) = -1 ECONNREFUSED (Connection refused)
[pid 11704] close(3)
更好的解決辦法將/lib/libbash_strip_env.so和/lib/libsandbox_web_ips.so,從/etc/ld.so.preload中剔除,但是它們就不能發揮作用了。
附1:Cron工作流
真正執行Cron命令的是vfork(有些版本為fork呼叫)出來的程序,寫標準輸出的fork是可選的,只有當存在“%”配置時才會生效,目的是通過寫標準輸出往vfork產生的程序輸入內容。
附2:SIGPIPE發生的位置
SIGPIPE正是發生在下圖所示的“send()”呼叫,至於是因為sap1012關閉了讀端,還是其它原因,暫不清楚原因。