比特币源码阅读(五)(初始化和启动(4))

AppInitBasicSetup() 函数定义的位置在src/init.cpp。由于函数较长并且是分步执行。所以将源代码拆分来看

// src/init.cpp // ********************************************************* Step 1: setup //如果是微软的VS环境就执行下面的内容 #ifdef _MSC_VER // Turn off Microsoft heap dump noise //将warn级别的内容都输出到文件(注意dump的报告级别即为warning) _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); //设置输出文件句柄,设置为NUL表示对告警信息不做任何处理 _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); // Disable confusing "helpful" text message on abort, Ctrl-C //指定当程序异常终止时要采取的操作。 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif

通过_CrtSetReportMode函数讲Warn级别信息输出到文件,然后再通过_CrtSetReportFile函数来指定文件的输出位置。设置为NUL表示忽略该级别的告警信息。_set_abort_behavior函数指定当程序异常终止时要采取的动作。参考:https://msdn.microsoft.com/zh-cn/e631wekh
// src/init.cpp#ifdef WIN32 // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), // which is not correct. Can be removed, when GCCs winbase.h is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE); #endif

数据执行保护 (DEP) 是一套软硬件技术,能够在内存上执行额外检查以帮助防止在系统上运行恶意代码。
这段代码是针对WIN32位系统中对DEP部分进行的设置。DEP最小支持的系统版本是WinXP SP3, WinVista >= SP1, Win Server 2008。
在这里定义的原因是winbase.h限制了必须满足_WIN32_WINNT >= 0x0601 (Windows 7)系统条件才会启用DEP,也就是说低版本不会启用DEP保护。
启用方法是通过动态链接库Kernel32.dll来查找SetProcessDEPPolicy的函数地址,并将PROCESS_DEP_ENABLE设置成0x00000001。也就是表示该进程的生命周期内永久启用DEP保护。
SetProcessDEPPolicy函数介绍参考:https://msdn.microsoft.com/en-us/library/bb736299(VS.85).aspx
接下来初始化网络套接字,函数定义在/src/util.cpp
bool SetupNetworking() { #ifdef WIN32 // Initialize Windows Sockets WSADATA wsadata; int ret = WSAStartup(MAKEWORD(2,2), &wsadata); if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2) return false; #endif return true; }

如果是Windows系统就需要初始化一些参数,不是Windows系统就返回true
// src/init.cpp #ifndef WIN32 if (!gArgs.GetBoolArg("-sysperms", false)) { umask(077); }// Clean shutdown on SIGTERM //终止信号 registerSignalHandler(SIGTERM, HandleSIGTERM); //来自键盘的中断信号 registerSignalHandler(SIGINT, HandleSIGTERM); // Reopen debug.log on SIGHUP //挂起信号 registerSignalHandler(SIGHUP, HandleSIGHUP); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif

这段代码先判断是否设置了-sysperms参数。如果设置了该参数就按照系统默认的权限创建新的文件。如果没有设置,则按照umask(077)来设置新文件的权限。umask(077)表示owner具有所有权限,group和other不对该文件具备任何权限
下面三行代码分别注册了终止信号、中断信号和挂起信号的处理函数,也就是把相应变量设置成true。代码如下所示
// src/init.cpp /** * Signal handlers are very limited in what they are allowed to do. * The execution context the handler is invoked in is not guaranteed, * so we restrict handler operations to just touching variables: */ static void HandleSIGTERM(int) { fRequestShutdown = true; }static void HandleSIGHUP(int) { fReopenDebugLog = true; }

接下来的signal(SIGPIPE, SIG_IGN) 表示忽略管道断开信号。作用是防止client通过socket连接到daemon之后client断开导致daemon终止的问题。signal相关内容可以参考http://www.cplusplus.com/reference/csignal/signal/。
我们来看最后一行代码 :
std::set_new_handler(new_handler_terminate);
参考:https://www.cnblogs.com/zhuyf87/archive/2013/04/04/2999916.html
当operator new 不能满足一个内存分配请求的时候,会抛出一个异常。而set_new_handler()函数的作用就是用来捕获这个异常,并且进行处理。
[[noreturn]] static void new_handler_terminate() { // Rather than throwing std::bad-alloc if allocation fails, terminate // immediately to (try to) avoid chain corruption. // Since LogPrintf may itself allocate memory, set the handler directly // to terminate first. std::set_new_handler(std::terminate); LogPrintf("Error: Out of memory. Terminating.\n"); // The log was successful, terminate now. std::terminate(); };

【比特币源码阅读(五)(初始化和启动(4))】代码显示直接终止进程,避免冲突。

    推荐阅读