比特币源码研读之九

本文将继续接着上一篇文章继续开展源码研读之旅,本文将完成初始化基本环境构建源码部分(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元!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容

  • iPhone的标准推荐是CFNetwork 库编程,其封装好的开源库是 cocoa AsyncSocket库,用它...
    Ethan_Struggle阅读 2,207评论 2 12
  • (九)继续看bitcoind.cpp中的132-136行 对它的注释为: InitError将被调用,并有详细的错...
    风来雾去阅读 1,807评论 0 1
  • 一、信号机制 函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间...
    feifei_fly阅读 7,866评论 1 14
  • 接上篇AppInit中,读到AppInitBasicSetup函数,这个函数的作用是注册相应的消息以及处理方式 1...
    NanoLeak阅读 308评论 0 0
  • 1. 感谢昨天做了一晚上梦,早上查看邮件,竟然“成真”,临时订机票,也算是提前做心理准备了,谢谢,谢谢,谢谢! 2...
    Katherine2018阅读 228评论 0 2