今天早上,例行巡查服務器,我用strace命令跟蹤服務器進程的時候, 發現有幾個服務器進程出現了死鎖情況, gdb繼續跟進,顯示如下:
gdb) bt
#0 0x00ff9410 in __kernel_vsyscall ()
#1 0x004d593e in __lll_mutex_lock_wait () from /lib/libc.so.6
#2 0x00465b38 in _L_lock_14080 () from /lib/libc.so.6
#3 0x00464df4 in free () from /lib/libc.so.6
#4 0x006c7691 in operator delete () from /usr/lib/libstdc++.so.6
#5 0x08059cfb in __gnu_cxx::new_allocator<std::_List_node<TTimeEvent> >::deallocate (this=0x98e0064, __p=0x98e1218)
at /usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/ext/new_allocator.h:94
#6 0x08059d20 in std::_List_base<TTimeEvent, std::allocator<TTimeEvent> >::_M_put_node (this=0x98e0064, __p=0x98e1218)
at /usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/bits/stl_list.h:320
#7 0x08059d81 in std::list<TTimeEvent, std::allocator<TTimeEvent> >::_M_erase (this=0x98e0064, __position=
{_M_node = 0x98e1218}) at /usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/bits/stl_list.h:1150
#8 0x08059db3 in std::list<TTimeEvent, std::allocator<TTimeEvent> >::pop_front (this=0x98e0064)
at /usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/bits/stl_list.h:747
#9 0x08059334 in CTimerManager::Process (this=0x98e0058) at src/timermanager.cpp:168
#10 0x080597dd in Process (nSigNo=14) at src/timermanager.cpp:199
#11 <signal handler called>
#12 0x004612ba in _int_free () from /lib/libc.so.6
#13 0x00464e00 in free () from /lib/libc.so.6
#14 0x006c7691 in operator delete () from /usr/lib/libstdc++.so.6
#15 0x006a424d in std::string::_Rep::_M_destroy () from /usr/lib/libstdc++.so.6
#16 0x0069e40f in std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >::~basic_stringbuf ()
from /usr/lib/libstdc++.so.6
#17 0x0069fd7f in std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::~basic_stringstream ()
from /usr/lib/libstdc++.so.6
#18 0x080524ea in CDBMoudle::Insert (this=0x98e6e80, tFixkey=@0xbfe1c6ec) at src/dbmoudle.cpp:59
#19 0x08051718 in CConnectionTask::ProcFixContent (this=0x98e6510) at src/connectiontask.cpp:218
#20 0x0805196e in CConnectionTask::HandleRead (this=0x98e6510) at src/connectiontask.cpp:86
#21 0x08051a0f in CConnectionTask::Handle (this=0x98e6510, nEvent=1) at src/connectiontask.cpp:52
#22 0x080585af in IServer::Run (this=0xbfe1c7e0) at src/server.cpp:133
#23 0x08055328 in main (argc=2, argv=0xbfe1c8b4) at src/main.cpp:19
在#13處,調用free函數, 然後進入與之相關的libc函數調用中.但是, 在這個調用還沒有完結之前被定時器管理模塊中斷, 進入了定時器處理的部分, 在在這個處理中同樣調用了free函數, 於是出現了死鎖的情況--因為malloc/free函數族不是可重入的, 在這裡有一篇相關的文章.
我曾經想在我的服務器代碼中盡量減少對象的構造/析構, 但是想了一下, 這個策略不是治本的辦法, 這意味著我必須在寫代碼的時候處處小心, 今天可能在A處出現死鎖, 明天可能會在B處出現.而且, 由於使用的是C++, 一些局部對象的構造和析構是不可避免的.
於是, 解決這個問題的思路就改變為:盡量的簡單化定時器處理操作.目前我想到的一個策略時, 一個定時器被觸發的時候, 置一個標志位, 而不是在在觸發的時候調用相應的處理函數, 然後在服務器的主循環中判斷是否被置位, 如果是的話再去調用相關的處理函數.
於是, 原來的思路就是:
// 該函數在定時器到時的時候被觸發
void signal()
{
// 定時器處理函數
dosomething();
}
while(1)
{
服務器主循環;
}
修改之後的思路是:
int violate g_alarm = 0;
// 該函數在定時器到時的時候被觸發
void signal()
{
g_alarm = 1;
}
while(1)
{
服務器主循環;
if (g_alarm)
{
// 定時器處理函數
dosomething();
g_alarm = 0;
}
}
這是大概的模型上面的改變.這是我目前能想到的處理該問題的最好辦法, 如果哪位有更好的辦法歡迎補充.