比特币源码解析(13)
来源:互联网 发布:申请阿里云邮箱 编辑:程序博客网 时间:2024/05/29 14:27
0x01 AppInitBasicSetup
本章我们继续分析AppInit()
中的下一个函数AppInitBasicSetup()
,从这个函数开始,源码将变量初始化的过程分了很多个Step,我们也按照这样的顺序来依次分析。
Step 1: Setup
#ifdef _MSC_VER // 如果是VS环境那么就执行 // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _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
首先的几行代码是针对微软的VS开发环境而设置的,其中_CrtSetReportMode
作用是设置消息的处理方式,该函数的详细介绍参考https://msdn.microsoft.com/zh-cn/library/1y71x448.aspx, 主要包含如下两个参数,
int _CrtSetReportMode( int reportType, // 报告的消息类型 int reportMode // 处理的模式);
对于第一个参数消息类型包括以下三种,
_CRT_WARN
警告、 消息和不需要立即关注的信息。 _CRT_ERROR
错误、 不可恢复的问题和需要立即关注的问题。 _CRT_ASSERT
断言失败 (断言表达式的计算结果为FALSE
)。针对这三种消息类型第二个参数可以指定不同的处理方式,
_CRTDBG_MODE_DEBUG
将消息写入调试器的输出窗口。 _CRTDBG_MODE_FILE
将消息写入用户提供的文件句柄。 _CrtSetReportFile应调用以定义要用作目标流的特定文件。 _CRTDBG_MODE_WNDW
创建一个消息框,以显示该消息以及Abort
, Retry
,和Ignore
按钮。 _CRTDBG_REPORT_MODE
返回reportMode
指定reportType
:1 _CRTDBG_MODE_FILE
2 _CRTDBG_MODE_DEBUG
4 _CRTDBG_MODE_WNDW
对于不同的消息类型我们可以同时指定不同的处理方式,例如对_CRT_WARN
指定输出到终端,对_CRT_ERROR
输出到文件;而如果指定的处理方式为写入文件即_CRTDBG_MODE_FILE
那么还需要调用_CrtSetReportFile
来设定输出的文件句柄,这也是代码的下一句,但是源码却将文件的路径设为NUL
也就是空文件,说明对于警告消息不做任何处理。
接下来一个函数_set_abort_behavior
是制定当程序异常终止时要采取的操作,也就是程序崩溃的时候如何处理。
转载: http://www.cppblog.com/woaidongmao/archive/2009/10/21/99129.html?opt=admin
很多软件通过设置自己的异常捕获函数,捕获未处理的异常,生成报告或者日志(例如生成mini-dump文件),达到Release版本下追踪Bug的目的。但是,到了VS2005(即VC8),Microsoft对CRT(C运行时库)的一些与安全相关的代码做了些改动,典型的,例如增加了对缓冲溢出的检查。新CRT版本在出现错误时强制把异常抛给默认的调试器(如果没有配置的话,默认是Dr.Watson),而不再通知应用程序设置的异常捕获函数,这种行为主要在以下三种情况出现。
(1) 调用abort函数,并且设置了_CALL_REPORTFAULT选项(这个选项在Release版本是默认设置的)。
(2) 启用了运行时安全检查选项,并且在软件运行时检查出安全性错误,例如出现缓存溢出。(安全检查选项 /GS 默认也是打开的)
(3) 遇到
_invalid_parameter
错误,而应用程序又没有主动调用_set_invalid_parameter_handler
设置错误捕获函数。所以结论是,使用VS2005(VC8)编译的程序,许多错误都不能在SetUnhandledExceptionFilter捕获到。这是CRT相对于前面版本的一个比较大的改变,但是很遗憾,Microsoft却没有在相应的文档明确指出。
虽然也可以通过
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT)
,signal(SIGABRT, ...)
和_set_invalid_parameter_handler(...)
解决(1)(3),但是对于(2),设置api hook是唯一的方式。
通过上述文章我们知道,源码中_set_abort_behavior
其实就是新版本的VS无法捕获到程序异常abort
时的异常,通过这个函数来使得用户可以捕获到从而进行处理。
#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
这段代码是针对winows 32位系统的系统中的DEP(Data Execution Prevention,数据执行保护),是WinXP SP3,WinVista >= SP1, Win Server 2008中加入为防止缓冲区溢出攻击的一种措施,保护特定内存中的数据不能当成代码一样执行。这里之所以启用的原因是在GCC中的winbase.h
中只有当系统版本满足_WIN32_WINNT >= 0x0601(Win 7)
时才会启用DEP,这就导致低版本就默认没有启用DEP。启用的方法是首先从动态链接库Kernel32.dll
中寻找SetProcessDEPPolicy的函数地址,该函数的介绍如下,
BOOL WINAPI SetProcessDEPPolicy( _In_ DWORD dwFlags);
dwFlags 是DWORD类型,可以取以下几种值,
源码中通过定义#define PROCESS_DEP_ENABLE 0x00000001
然后传入SetProcessDEPPolicy
中来启用为进程启用DEP。
接下来通过SetupNetworking()
来初始化套接字,该函数的实现位于src/init.cpp line 863
,
// src/init.cpp line 863bool 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使用Socket之前都需要先进行初始化,而这个SetupNetworking()
就是起的这个作用。如果不是Windows环境那么就直接返回true
。
#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
接下来这段代码是在非Windows环境下执行的,首先来看看帮助信息对-sysperms
的解释,
Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)。
以系统的默认权限创建新文件,而不是077,此功能只有在禁用钱包功能的情况下才有效。
如果设置了-sysperms
那么就以系统默认权限创建新的文件,如果没有设置该参数,那么通过umask
来设置创建新文件时的权限,umask(077)
表示创建的文件具有读写权限,创建的目录ower具有所有权限,所属组和其他用户没有任何权限。
然后三行代码分别注册了终止、中断以及挂起信号的处理函数,而这两个函数做的内容就是将相应的变量设置为true
,
// src/init.cpp line 287static void HandleSIGTERM(int){ fRequestShutdown = true;}static void HandleSIGHUP(int){ fReopenDebugLog = true;}
而fRequestShutdown
在Step 7中我们会发现是一个循环的控制变量while (!fLoaded && !fRequestShutdown) // init.cpp line 1389
,当变量为true
时才终止循环,这也是SIGTERM
信号要做的事。
接下来的signal(SIGPIPE, SIG_IGN);
表示忽略管道断开信号,首先介绍一下signal
函数,
参考: http://www.cplusplus.com/reference/csignal/signal/
signal函数是用来设置对应信号的处理函数,包括两个参数(in sig, void(*func)(int))。
第一个参数sig表示要处理的信号,有以下一些取值(参考:http://blog.csdn.net/ta893115871/article/details/7475095),
Signal Description SIGABRT 由调用abort函数产生,进程非正常退出 SIGALRM 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时 SIGBUS 某种特定的硬件异常,通常由内存访问引起 SIGCANCEL 由Solaris Thread Library内部使用,通常不会使用 SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略 SIGCONT 当被stop的进程恢复运行的时候,自动发送 SIGEMT 和实现相关的硬件异常 SIGFPE 数学相关的异常,如被0除,浮点溢出,等等 SIGFREEZE Solaris专用,Hiberate或者Suspended时候发送 SIGHUP 发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送 SIGILL 非法指令异常 SIGINFO BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程 SIGINT 由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 SIGIO 异步IO事件 SIGIOT 实现相关的硬件异常,一般对应SIGABRT SIGKILL 无法处理和忽略。中止某个进程 SIGLWP 由Solaris Thread Libray内部使用 SIGPIPE 在reader中止之后写Pipe的时候发送 SIGPOLL 当某个事件发送给Pollable Device的时候发送 SIGPROF Setitimer指定的Profiling Interval Timer所产生 SIGPWR 和系统相关。和UPS相关。 SIGQUIT 输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程 SIGSEGV 非法内存访问 SIGSTKFLT Linux专用,数学协处理器的栈异常 SIGSTOP 中止进程。无法处理和忽略。 SIGSYS 非法系统调用 SIGTERM 请求中止进程,kill命令缺省发送 SIGTHAW Solaris专用,从Suspend恢复时候发送 SIGTRAP 实现相关的硬件异常。一般是调试异常 SIGTSTP Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程 SIGTTIN 当Background Group的进程尝试读取Terminal的时候发送 SIGTTOU 当Background Group的进程尝试写Terminal的时候发送 SIGURG 当out-of-band data接收的时候可能发送 SIGUSR1 用户自定义signal 1 SIGUSR2 用户自定义signal 2 SIGVTALRM setitimer函数设置的Virtual Interval Timer超时的时候 SIGWAITING Solaris Thread Library内部实现专用 SIGWINCH 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程 SIGXCPU 当CPU时间限制超时的时候 SIGXFSZ 进程超过文件大小限制 SIGXRES Solaris专用,进程超过资源限制的时候发送第二个参数表示处理的函数,取值有以下三种,
SIG_DFL 默认处理,交给系统默认处理方式处理 SIG_IGN 忽略该信号 Function handler 自定义函数处理再来详细介绍一下SIGPIPE信号,
转载:http://www.cppblog.com/elva/archive/2008/09/10/61544.html
“当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把SIGPIPE设为SIG_IGN。”
回到我们的源码,现在明白了这里是防止client通过socket连接到daemon之后client断开导致daemon终止的问题,而忽略SIGPIPE这个信号就可以解决。
接下来看最后一句代码,
std::set_new_handler(new_handler_terminate);
参考:http://www.cnblogs.com/lidan/archive/2012/02/18/2357698.html
set_new_handler()
函数的功能是设置当operator new无法满足某一内存分配需求时首先调用的处理函数,而源码中对new_handle_terminate
的定义是
[[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();};
我们发现处理方法是直接终止进程,从而避免可能的数据冲突。
- 比特币源码解析(13)
- 比特币源码解析(1)
- 比特币源码解析(2)
- 比特币源码解析(3)
- 比特币源码解析(4)
- 比特币源码解析(5)
- 比特币源码解析(6)
- 比特币源码解析(7)
- 比特币源码解析(8)
- 比特币源码解析(9)
- 比特币源码解析(11)
- 比特币源码解析(12)
- 比特币源码解析(14)
- 比特币源码解析(15)
- 比特币源码解析(16)
- 比特币源码解析(17)
- 比特币源码解析(18)
- 比特币源码解析(19)
- 当前流行的J2EE WEB应用架构分析
- EventBus传值(Fragment和Activity,Activity和Activity)
- xcode-instrument
- ssm读写分离
- SCUT Training 20170913 Problem D
- 比特币源码解析(13)
- C# 编写Windows Service(windows服务程序)
- 静态方法与非静态方法的区别
- [React]调用系统命令,替换文件内容,并且提交到git仓库
- (二)、logback + slf4j
- Sql性能优化梳理
- leetcode 111. Minimum Depth of Binary Tree DFS深度优先遍历 + 添加对叶子节点判断
- js-某个字段有值即把该行用颜色标记
- Error:Execution failed for task ':caldroid:compileDebugJavaWithJavac'. > Cannot find System Java Com