比特幣源碼研讀之九

本文將繼續接著上一篇文章繼續開展源碼研讀之旅,本文將完成初始化基本環境構建源碼部分(AppInitBasicSetup)的講解。

本文主要涉及的源碼文件包括:

src/bitcond.cpp、src/util.h、src/util.cpp、src/init.h、src/init.cpp

一、警告消息處理

警告消息處理函數實現代碼很簡單,只有5行代碼

#ifdef _MSC_VER

// Turnoff Microsoft heap dump noise

_CrtSetReportMode(_CRT_WARN,_CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_WARN,CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));

#endif

通過#ifdef _MSC_VER,我們可以知道上面這段代碼是針對微軟的VS開發環境而設置的,在其他編譯環境下是這段代碼是不會執行的。上述這段代碼中調用了2個開發環境相關的函數_CrtSetReportMode和_CrtSetReportFile。

(1)_CrtSetReportMode函數定義如下:

int _CrtSetReportMode(

intreportType,

intreportMode

);

其包含的兩個參數分別為報告類型和報告輸出模式:

報告類型包含:

l_CRT_WARN:警告、消息和不需要立即關注的信息。

l_CRT_ERROR:錯誤、不可恢復的問題和需要立即關注的問題。

l_CRT_ASSERT:斷言失敗(斷言表達式的計算結果為FALSE)。。

報告模式包含:

l_CRTDBG_MODE_DEBUG:將消息寫入調試器的輸出窗口。

l_CRTDBG_MODE_FILE:將消息寫入用戶提供的文件句柄。_CrtSetReportFile應調用以定義要用作目標流的特定文件。

l_CRTDBG_MODE_WNDW:創建一個消息框,以顯示該消息以及Abort,Retry,和Ignore按鈕。

l_CRTDBG_REPORT_MODE:返回reportMode指定reportType:1?_CRTDBG_MODE_FILE、2_CRTDBG_MODE_DEBUG、4_CRTDBG_MODE_WNDW

由上述分析可以知道當前代碼中設置的報告類型為警告,報告輸出方式為文件輸出。

(2)_CrtSetReportFile函數定義如下:

_HFILE _CrtSetReportFile(

int reportType,

_HFILE reportFile

);

參數reportType與_CrtSetReportMode中的參數一致,都是_CRT_WARN,?_CRT_ERROR, and?_CRT_ASSERT3種類型,這里就不需要贅述了。

參數reportFile為文件句柄,其對應的文件作為reportType相應消息的輸出對象。

在當前代碼中使用CreateFileA("NUL", GENERIC_WRITE, 0,NULL, OPEN_EXISTING, 0, 0)實現了輸出文件的創建。但是其第一個參數值(文件名)為“NULL”,則說明這個文件為空,警告消息雖然說是輸出至文件中,但當前為空文件,那么警告消息的輸出可理解為被關閉了。這點可從代碼注釋找到依據:// Turn off Microsoft heap dump noise,即關閉微軟內存堆快照的“噪音”,這里的噪音應該就是指此處的警告消息了。

二、abort函數調用處理

很多軟件通過設置自己的異常捕獲函數,捕獲未處理的異常,生成報告或者日志(例如生成mini-dump文件),達到Release版本下追蹤Bug的目的。但是,到了VS2005(即VC8),Microsoft對CRT(C運行時庫)的一些與安全相關的代碼做了些改動,典型的,例如增加了對緩沖溢出的檢查。新CRT版本在出現錯誤時強制把異常拋給默認的調試器(如果沒有配置的話,默認是Dr.Watson),而不再通知應用程序設置的異常捕獲函數,這種行為主要在以下3種情況出現。

1調用abort函數,并且設置了_CALL_REPORTFAULT選項(這個選項在Release版本是默認設置的);

(2)啟用了運行時安全檢查選項,并且在軟件運行時檢查出安全性錯誤,例如出現緩存溢出。(安全檢查選項/GS默認也是打開的);

(3)遇到_invalid_parameter錯誤,而應用程序又沒有主動調用_set_invalid_parameter_handler設置錯誤捕獲函數。

所以結論是,使用VS2005(VC8,代碼中的宏定義為_MSC_VER >= 1400)編譯的程序,許多錯誤都不能在SetUnhandledExceptionFilter捕獲到。這是CRT相對于前面版本的一個比較大的改變,但是很遺憾,Microsoft卻沒有在相應的文檔明確指出。

我們可以通過_set_abort_behavior(0, _WRITE_ABORT_MSG |_CALL_REPORTFAULT)解決(1),也就是abort函數異常錯誤捕獲問題。

詳細解釋參考:http://blog.csdn.net/yuzhiyuxia/article/details/16889155

三、數據執行保護(DEP)功能處理

數據執行保護(DEP)的目的是為了防止病毒或其他安全威脅造成損害,Windows XP SP2、WinXP SP3, WinVista >= SP1, Win Server

2008使用了數據執行保護(DEP)功能,而GCCs winbase.h將該功能限制在_WIN32_WINNT >= 0x0601 (Windows 7)才能使用,所以在代碼中強制定義了宏定義

#ifndef PROCESS_DEP_ENABLE

#define PROCESS_DEP_ENABLE 0x00000001

#endif

并且通過函數指針獲取Kernel32.dll中的SetProcessDEPPolicy函數對象,實現DEP功能的開啟。

typedef BOOL (WINAPI*PSETPROCDEPPOL)(DWORD);

PSETPROCDEPPOL setProcDEPPol =(PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"),"SetProcessDEPPolicy");

if (setProcDEPPol != NULL)

setProcDEPPol(PROCESS_DEP_ENABLE);

四、初始化網絡連接

SetupNetworking函數在src/util.cpp中實現,其主要實現Winsock服務的初始化,初始化的工作通過WSAStartup函數完成。這一步的執行是必須在初始化中完成的,具體原因如下:

為了在應用程序當中調用任何一個Winsock API函數,首先第一件事情就是必須通過WSAStartup函數完成對Winsock服務的初始化,因此需要調用WSAStartup函數。使用Socket的程序在使用Socket之前必須調用WSAStartup函數。該函數的第一個參數指明程序請求使用的Socket版本,其中高位字節指明副版本、低位字節指明主版本;操作系統利用第二個參數返回請求的Socket的版本信息。當一個應用程序調用WSAStartup函數時,操作系統根據請求的Socket版本來搜索相應的Socket庫,然后綁定找到的Socket庫到該應用程序中。以后應用程序就可以調用所請求的Socket庫中的其它Socket函數了。

總得來說,Socket連接涉及的API調用,都必須在WSAStartup函數執行之后才有效,否則將無法執行網絡連接操作。

五、信號處理設置

通過宏定義判斷#ifndef WIN32,我們可以知道信號處理設置是針對非Windows系統的。信號處理設置代碼可分為4部分來分析:

(1)文件創建權限

if (!GetBoolArg("-sysperms",false)) {

umask(077);

}

在該代碼中,程序首先判斷是否設置了sysperms參數,該參數的的含義為:

Create new files with system default permissions,instead of umask 077 (only effective with disabled wallet functionality)

在創建新文件時,文件權限為系統默認權限,以此來代替umask 077命令(因為umask 077只在錢包功能被禁止時才其作用)

如果設置了則返回其狀態值,如果為false,則需執行umask(077)命令。

umask用于設置文件與文件夾使用權限,此處077代表---rwxrwx,表示owner沒有任何權限,group和other有完全的操作權限。

(2)進程終止信號處理

進程終止信號處理代碼如下所示:

// Clean shutdown on SIGTERM

struct sigaction sa; //信號處理對象

sa.sa_handler = HandleSIGTERM; //進程終止信號處理句柄

sigemptyset(&sa.sa_mask);

sa.sa_flags = 0;

sigaction(SIGTERM, &sa, NULL); //終止信號處理

sigaction(SIGINT, &sa, NULL); //中斷信號處理

從代碼可以看到,程序首先定義了信號處理對象sa,然后對其句柄、標志和掩碼賦值,最后將該信號對象傳遞給終止與中斷信號處理函數。

這里要說明的是句柄函數HandleSIGTERM,該函數在src/init.cpp中實現,其實現代碼為:

void HandleSIGTERM(int)

{

fRequestShutdown = true;

}

很簡單的一個函數,將全局變量fRequestShutdown設置為true,所有正在運行的線程將根據一定的規則停止運行。

(3)掛起信號處理

進程掛起信號處理代碼如下所示:

// Reopen debug.log on SIGHUP

struct sigaction sa_hup;

sa_hup.sa_handler = HandleSIGHUP; //掛起信號處理句柄函數

sigemptyset(&sa_hup.sa_mask);

sa_hup.sa_flags = 0;

sigaction(SIGHUP, &sa_hup, NULL); //掛起信號處理

從代碼可以看出掛起信號處理過程與終止信號處理過程是一樣的。這里要說明的是句柄函數HandleSIGHUP,該函數在src/init.cpp中實現,其實現代碼為:

void HandleSIGHUP(int)

{

fReopenDebugLog = true;

}

很簡單的一個函數,將全局變量fReopenDebugLog設置為true,src/util.cpp中的LogPrintStr將重新打開調試日志打印文件。

(4)管道錯誤處理

管道錯誤處理只有一行代碼:

signal(SIGPIPE, SIG_IGN);

signal為信號函數,第一個參數表示需要處理的信號值(SIGPIPE,管道錯誤),第二個參數為處理函數或者是一個表示,此處SIG_IGN表示忽略SIGPIPE那個注冊的信號。

此處需設置忽略SIGPIPE管道錯誤,否則客戶端異常關閉時會將守護進程連帶著也給關閉,影響守護進程的正常運行。

六、內存分配失敗處理

內存分配失敗處理函數主要由set_new_handler函數完成,該函數說明如下:

1.???set_new_handler函數的作用是設置new_p指向的函數為new操作或new[]操作失敗時調用的處理函數。

2.設置的處理函數可以嘗試使更多空間變為可分配狀態,這樣新一次的new操作就可能成功。當且僅當該函數成功獲得更多可用空間它才會返回;否則它將拋出bad_alloc異常(或者繼承該異常的子類)或者終止程序(例如調用abort或exit)。

3.如果設置的處理函數返回了(例如,該函數成功獲得了更多的可用空間),它可能將被反復調用,直到內存分配成功,或者它不再返回,或者被其它函數所替代。

4.在尚未用set_new_handler設置處理函數,或者設置的處理函數為空時,將調用默認的處理函數,該函數在內存分配失敗時拋出bad_alloc異常。

從上述說明我們可以看出,set_new_handler需要一個內存分配失敗后的處理函數,源碼中通過new_handler_terminate()函數完成了該功能,通過new_handler_terminate函數中的注釋我們可以知道其為了防止影響區塊鏈被破壞,通過執行terminate命令,直接終止程序的方式解決內存分配失敗導致的錯誤,并且進行日志打印。

作者:區塊鏈研習社比特幣源碼研讀班 菜菜子


以下是廣告:

我們區塊鏈研習社已創建“區塊鏈研習社幣圈交流”小密圈”,在小密圈中,我們將帶領大家一起學習區塊鏈的原理與投資,還將提供區塊鏈基本原理解答、交易所注冊與交易操作、ICO交易與操作、投資分析、風險分析等內容。

目前入圈價格初始定價50元,50人調整一次價格,每次調整幅度為50元!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • iPhone的標準推薦是CFNetwork 庫編程,其封裝好的開源庫是 cocoa AsyncSocket庫,用它...
    Ethan_Struggle閱讀 2,277評論 2 12
  • (九)繼續看bitcoind.cpp中的132-136行 對它的注釋為: InitError將被調用,并有詳細的錯...
    風來霧去閱讀 1,858評論 0 1
  • 一、信號機制 函數運行在用戶態,當遇到系統調用、中斷或是異常的情況時,程序會進入內核態。信號涉及到了這兩種狀態之間...
    feifei_fly閱讀 8,258評論 1 14
  • 接上篇AppInit中,讀到AppInitBasicSetup函數,這個函數的作用是注冊相應的消息以及處理方式 1...
    NanoLeak閱讀 328評論 0 0
  • 1. 感謝昨天做了一晚上夢,早上查看郵件,竟然“成真”,臨時訂機票,也算是提前做心理準備了,謝謝,謝謝,謝謝! 2...
    Katherine2018閱讀 260評論 0 2