仍是面某M的時候,面試官問我:「用過gdb麼?」 答:「用過,調了兩年bug了」。「那好,給我解釋下gdb是怎麼工做的?或者說跟內核什麼地方有關係?」。 html
是阿,gdb憑什麼能夠調試一個程序?憑什麼可以接管一個程序的運行?我之前也想過這樣的問題,可是後來竟然忘記去查看了。我想到了咱們的二進制翻譯器,想到了intel的pin,Dynamo。這些都是將翻譯後的代碼放到codecache中去運行,而後接管整個程序的執行。gdb是否是也同樣呢? linux
若是真是這樣,爲何我記得用gdb跑一個程序,這個程序會有一個單獨的進程?gdb的attach功能又是怎麼實現的? 面試
想了想,我仍是沒有答上來。面試就是由這麼一個又一個細節的小杯具最後聚集成一個大杯具。 多線程
那麼,gdb究竟是憑什麼接管的一個進程的執行呢?其實,很簡單,經過一個系統調用:ptrace。ptrace系統調用的原型以下: 函數
#include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); |
說明:ptrace系統調用提供了一種方法來讓父進程能夠觀察和控制其它進程的執行,檢查和改變其核心映像以及寄存器。 主要用來實現斷點調試和系統調用跟蹤。(man手冊) spa
其實,說到這裏,一切原理層面應該都比較明朗了(且先不去管內核中是怎麼實現ptrace的)。gdb就是調用這個系統調用,而後經過一些參數來控制其餘進程的執行的。 線程
下面咱們來看ptrace函數中request參數的一些主要選項: 翻譯
PTRACE_TRACEME: 表示本進程將被其父進程跟蹤,交付給這個進程的全部信號,即便信號是忽略處理的(除SIGKILL以外),都將使其中止,父進程將經過wait()獲知這一狀況。 調試
這是什麼意思呢?咱們能夠結合到gdb上來看。若是在gdb中run一個程序,首先gdb會fork一個子進程,而後該子進程調用ptrace系統調用,參數就是PTRACE_TRACEME,而後調用一個exec執行程序。基本過程是這樣,細節上可能會有出入。須要注意的是,這個選項PTRACE_TRACEME是由子進程調用的而不是父進程! code
如下選項都是由父進程調用:
PTRACE_ATTACH:attach到一個指定的進程,使其成爲當前進程跟蹤的子進程,而子進程的行爲等同於它進行了一次PTRACE_TRACEME操做。可是,須要注意的是,雖然當前進程成爲被跟蹤進程的父進程,可是子進程使用getppid()的到的仍將是其原始父進程的pid。
這下子gdb的attach功能也就明朗了。當你在gdb中使用attach命令來跟蹤一個指定進程/線程的時候,gdb就自動成爲改進程的父進程,而被跟蹤的進程則使用了一次PTRACE_TRACEME,gdb也就瓜熟蒂落的接管了這個進程。
PTRACE_CONT:繼續運行以前中止的子進程。可同時向子進程交付指定的信號。
這個選項呢,其實就至關於gdb中的continue命令。當你使用continue命令以後,一個被gdb中止的進程就能繼續執行下去,若是有信號,信號也會被交付給子進程。
除了以上這幾個選項,ptrace還有不少其餘選項,能夠在linux下閱讀man手冊:man ptrace
須要注意的另外一點是,使用gdb調試過多線程/進程的人應該都知道,當子進程遇到一個信號的時候,gdb就會截獲這個信號,並將子進程暫停下來。這是爲何呢?
實際上,在使用參數爲PTRACE_TRACEME或PTRACE_ATTACH的ptrace系統調用創建調試關係以後,交付給目標程序的任何信號(除SIGKILL以外)都將被gdb先行截獲,或在遠程調試中被gdbserver截獲並通知gdb。gdb所以有機會對信號進行相應處理,並根據信號的屬性決定在繼續目標程序運行時是否將以前截獲的信號實際交付給目標程序。
參考資料:gdb的基本工做原理
《gdb pocket reference》