1. 程式人生 > >比特幣原始碼解析(9)

比特幣原始碼解析(9)

0x00 摘要

  • bitcoin-cli:是Bitcoind的一個功能完備的RPC客戶端,包括查詢區塊,交易資訊等等,具體將在相應章節介紹。
  • bitcoind:是比特幣執行的核心程式俗稱bitcoin core,也是我們分析的重點。
  • bitcoin-qt:比特幣錢包。
  • bitcoin-tx:比特幣交易處理模組,支援交易的查詢和建立。
  • test_bitcoin:執行各個模組的測試程式碼。
  • test_bitcoin-qt:執行錢包的模組測試程式碼。

我們首先從最核心的bitcoind開始分析,然後再看其他的,因為其他部分的程式碼使用的很多類、很多函式都是bitcoind中使用過的,所以分析完bitcoind,其他部分也就輕而易舉。

另外提及一下程式碼的檢視軟體,我用的是Sublime Text,能快速的找到函式的定義和實現的位置,並且還支援在專案內查詢,一個好的編輯器對於程式碼的分析也是很有幫助的。

0x01 Main

對於c++程式碼,整個程式都是從main函式開始執行的,所以我們首先尋找bitcoind的main函式。而一般編譯出來的可執行程式都是有對應檔名的cpp檔案,所以我們找到了src/bitcoind.cpp,程式碼拉到最後就找到了我們的main函式。

// src/bitcoind.cpp line 188
int main(int argc, char* argv[])
{
    SetupEnvironment()
; // Connect bitcoind signal handlers noui_connect(); return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE); }

0x02 SetupEnvironment

找到SetupEnvironment的實現位置,位於src/util.cpp中,sublime中直接右鍵Goto Definition即可。

// src/util.cpp line 834
void SetupEnvironment()
{
#ifdef HAVE_MALLOPT_ARENA_MAX
if (sizeof(void*) == 4) { //判斷是否為32位系統 mallopt(M_ARENA_MAX, 1); } #endif #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) try { std::locale(""); // Raises a runtime error if current locale is invalid } catch (const std::runtime_error&) { setenv("LC_ALL", "C", 1); } #endif std::locale loc = fs::path::imbue(std::locale::classic()); fs::path::imbue(loc); }

函式首先通過sizeof(void*) == 4來判斷當前系統是否是32位,如果是64位的話那麼sizeof(void*)值就為8。mallopt函式是用來控制malloc記憶體分配時的行為的(具體請參考http://man7.org/linux/man-pages/man3/mallopt.3.html),而M_ARENA_MAX引數是值最多能建立的arena數,一個arena是指malloc在分內記憶體時的一個記憶體池,而這個arena是執行緒安全的,也就是說多執行緒訪問時是互斥訪問的,既然是互斥訪問的,那麼很明顯,當arena數量越多時,執行緒的競爭就越小,但是需要的記憶體也就越多(因為arena就相當於一次性申請大量記憶體,然後在malloc時慢慢分配出去)。通過程式碼中的註釋,我們發現glibc庫會為每個核建立2個arena,而這會對32為系統造成虛擬地址空間不足的問題,所以這裡設為1.

下面是locale()是設定系統區域,這將決定程式所使用的當前語言編碼、日期格式、數字格式及其它與區域有關的設定。最後兩行是檔案路徑的本地化設定,主要設計寬字元(Wide char)和多位元組(Multi bytes)之間的轉換問題。

0x03 noui_connect

// src/noui.cpp line 52
void noui_connect()
{
    // Connect bitcoind signal handlers
    uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
    uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);
    uiInterface.InitMessage.connect(noui_InitMessage);
}
// src/ui_interface.h 
// line 74-81

    /** Show message box. */
    boost::signals2::signal<bool (const std::string& message, 
                                  const std::string& caption, 
                                  unsigned int style), 
        boost::signals2::last_value<bool>> ThreadSafeMessageBox;

    /** If possible, ask the user a question. 
    * If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
    boost::signals2::signal<bool (const std::string& message, 
                                  const std::string& noninteractive_message, 
                                  const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeQuestion;

    /** Progress message during initialization. */
    boost::signals2::signal<void (const std::string &message)> InitMessage;

extern CClientUIInterface uiInterface;  // line 123

這裡在CClientUIInterface類中定義了一些訊號,其中三個分別是ThreadSafeMessageBoxThreadSafeQuestionInitMessage。再看前面的noui_connect中的變數,我們發現通過connect連線的插槽函式定義和訊號中的定義完全一致,所以當訊號觸發的時候,這些連線的函式都會被呼叫。