一個死鎖分析過程

 

一小夥說他的程序死鎖了,讓幫忙看看。對死鎖問題,首先祭出GDB這一神器。

(gdb) bt

#0  0xffffe410 in __kernel_vsyscall ()

#1  0xf7fa790e in __lll_mutex_lock_wait () from /lib/libpthread.so.0

#2  0xf7fa3a8b in _L_mutex_lock_81 () from /lib/libpthread.so.0

#3  0xf7fa35be in pthread_mutex_lock () from /lib/libpthread.so.0

#4  0x08316692 in mooon::sys::CLock::lock (this=0x89f59e0)

 

第一步的目標是找到鎖被誰持有了,這只需要找到死鎖的位置,然後查看pthread_mutex_t.__owner值是什麼。接下來使用GDB的“info threads”命令找到持有的線程。

找到持有的線程後,查看它的調用棧:

#0  0xffffe410 in __kernel_vsyscall ()

#1  0xf7daea16 in __gxx_personality_v0 () from /lib/libc.so.6

#2  0xf7ddccbc in usleep () from /lib/libc.so.6

#3  0x0825a374 in CSHMPkg::LockWait (this=0xddb3ebb0) at SHMPkg.cpp:221

 

這個時候需要看對應的源代碼:

bool CSHMPkg::LockWait()

{

  if (strlen(m_lockFilePath) <= 0) return false;

  while (!m_readLock.LockFile(m_lockFilePath))

    usleep(10);

  return true;

}

 

進一步看加文件鎖的代碼:

bool CFileLock::LockFile(const char* filePath)

{

  m_fd = open(filePath, O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);

  if(m_fd == -1)

    return false;

  if (write_lock(m_fd, 0, SEEK_SET, 1) == -1) {

    close(m_fd);

    m_fd = -1;

    return false;

  }

  return true;

}

 

初步估計while處死循環了,在usleep處打個斷點可輕鬆確定是否死循環。結果確認這裏是死循環了。因此問題在LockFile函數,它應當是返回了false。通過單步調試,發現是open失敗。檢查文件filePath存在,且權限正常,需要找出open失敗的原因。

因爲open是個系統調用,因此只需要知道errno值即可知道原因。但errno是個宏,不能直接打印,找到宏errno的定義:

#   define errno (*__errno_location ())

 

在GDB中調用__errno_location(),發現errno的值爲24

#define EMFILE 24 /* Too many open files */

 

到這裏,問題已清楚,執行shell命令ulimit -a查看,發現“open files”值爲1024,在當下的服務器,1024是一個比較小的值。程序中大量採用redis集羣直連,對fd的需求很大,進一步執行“netstat -antp”或lsof也可以看到。

因此解決辦法是調大“open files”值,臨時可使用ulimit調大,但一般還是得修改文件limits.conf。有關limits.conf,可閱讀另一博文《源碼解讀Linux的limits.conf文件》。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章