[Linux] 卸載 DLL 時,libcurl 的背景 thread 造成 crash?
最近一直在查一個問題,
專案的程式在 unload 再 reload 某個 libX.so (化名)之後,
就很容易當掉 (crash)…
而且這個問題只會發生在 libX 在做網路連線,但連線逾時 (timeout) 的情況下…
如果網路連線正常,reload 是不會有問題的…
查了半天,目前是懷疑 libcurl 造成的問題…
因為 libX.so 會用 libcurl 做 HTTP 查詢,
從 curl_global_cleanup 說明網頁來看,它不是 thread-safe 的,
而且看來 libcurl 會自己偷偷建 thread,
如果 libX.so 被 unload 時,libcurl 自建的 thread 還在時,可能會 crash:
curl_global_cleanup does not block waiting for any libcurl-created threads to terminate (such as threads used for name resolving). If a module containing libcurl is dynamically unloaded while libcurl-created threads are still running then your program may crash or other corruption may occur. We recommend you do not run libcurl from any module that may be unloaded dynamically.
用 gdb 觀察一下 thread 建立的情形,似乎有符合這邊的敘述。
首先當我們 load libX.so 時,它會建立 5 根 thread,這是預期的:
[New Thread 0x7f59ab7fe700 (LWP 65115)] [New Thread 0x7f59aaffd700 (LWP 65116)] [New Thread 0x7f59aa7fc700 (LWP 65117)] [New Thread 0x7f59a9ffb700 (LWP 65118)] [New Thread 0x7f59a97fa700 (LWP 65119)]
libX.so 做完事情之後,這 5 根 thread 正常結束了:
[Thread 0x7f59a97fa700 (LWP 65119) exited] [Thread 0x7f59a9ffb700 (LWP 65118) exited] [Thread 0x7f59aa7fc700 (LWP 65117) exited] [Thread 0x7f59aaffd700 (LWP 65116) exited] [Thread 0x7f59ab7fe700 (LWP 65115) exited]
但這時有一根不知名的 thread 65264 被跑起來了:
[New Thread 0x7f59ab7fe700 (LWP 65264)]
如果這時去 unload 掉 libX.so,這根不知名 thread 就會 crash,
可能是因為跑到 curl_global_cleanup() 的關係:
Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7f59ab7fe700 (LWP 65264)] (gdb) bt #0 0x00007f5a10e127ef in ?? () #1 0x00007f59e0017e08 in ?? () #2 0x00007f59e0017e10 in ?? () #3 0x00007f59ab7fde90 in ?? () #4 0x00007f59e0017e80 in ?? () #5 0x00007f5a31fa28c0 in ?? () #6 0x00007f5a30e5db90 in ?? () from /lib64/libc.so.6 #7 0x00007f59a9ffb9c0 in ?? () #8 0x0000000000000000 in ?? ()
如果在那不知名 therad 結束之後,才去 reload libX.so,就不會遇到 crash…
以上還屬於推測階段,但感覺上很像是 curl_global_cleanup 提到的問題。
以 libcurl 的建議來說,就是不要去動態 unload 掉 libX.so,
但我們的程式就是得這麼做,所以不是可接受的解法…
就先把這個問題記在這裡囉…
[2018/4/20 後記]
後來確認問題是因為新版的 curl 預設將 threaded resolver 打開,
因此 curl 會多產生一根 thread 來問 DNS。
由於本例中是 DNS 會問不到而逾時,
因此這根 thread 在逾時前都會存在,
導致 curl_global_cleanup() 執行時 crash…
簡單的解決方法是關掉 threaded resolver,
在編譯 curl 時,加上 –disable-threaded-resolver 選項,例如:
./configure --disable-threaded-resolver
這樣子 curl 就不會產生一個 DNS query thread 了~