比特币源码阅读(五):初始化和启动(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();
};
代码显示直接终止进程,避免冲突。