读比特币源码4

接上篇AppInit中,读到AppInitBasicSetup函数,这个函数的作用是注册相应的消息以及处理方式

1、如果是微软VS环境,通过_CrtSetReportMode和_CrtSetReportFile,关闭微软堆栈转储的噪音,
_CrtSetReportMode设置消息处理的方式,此次设置消息级别为WARN,并输出到文件,
_CrtSetReportFile设置消息输出的文件,此处设置为NUL,即忽略消息。
_set_abort_behavior函数指定当程序非正常终止时的行为,用该函数把异常抛给异常捕获函数
参考:https://technet.microsoft.com/zh-cn/library/1y71x448.aspx/
https://msdn.microsoft.com/en-us/library/e631wekh.aspx


    // ********************************************************* Step 1: setup
#ifdef _MSC_VER
    // 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

2、下面这段代码是针对winows 32位系统的系统中的DEP(Data Execution Prevention,数据执行保护),最低支持的os系统版本时WinXP SP3,WinVista >= SP1, Win Server 2008,设置失败不紧要,不需要进一步关注。
因为在GCC 的winbase.h限制_WIN32_WINNT >= 0x0601 (Windows 7)才会启用DEP,此处强制启用。
定义指向SetProcessDEPPolicy的函数指针,并设置DEP。

#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

3、SetupNetworking初始化windows sockets

    if (!SetupNetworking())
        return InitError("Initializing networking failed");

实现如下,
在win32系统中使用Socket的程序在使用Socket之前必须调用WSAStartup函数。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
例:假如一个程序要使用2.2版本的Socket,那么程序代码如下
wVersionRequested = MAKEWORD( 2, 2);
err = WSAStartup( wVersionRequested, &wsaData );

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;
}

4、在非win32系统中,如果-sysperms参数设置为false,则设置umask为077,遇到SIGTERM和SIGINT信号时设置关闭标志为true,遇到SIGHUP挂断终端信号,则重新开启debuglog,SIGPIPE信号则忽略,否则如果客户端异常终止,daemon进程也会停止运行。

static void HandleSIGTERM(int)
{
    fRequestShutdown = true;
}
static void HandleSIGHUP(int)
{
    fReopenDebugLog = true;
}

SIGTERM 终止进程 软件终止信号
SIGTERM是杀或的killall命令发送到进程默认的信号。它会导致一过程的终止,但是SIGKILL信号不同,它可以被捕获和解释(或忽略)的过程。因此,SIGTERM类似于问一个进程终止可好,让清理文件和关闭。因为这个原因,许多Unix系统关机期间,初始化问题SIGTERM到所有非必要的断电过程中,等待几秒钟,然后发出SIGKILL强行终止仍然存在任何这样的过程。

SIGINT 终止进程 中断进程
符合POSIX平台,信号情报是由它的控制终端,当用户希望中断该过程发送到处理的信号。通常ctrl-C,但在某些系统上,“删除”字符或“break”键 - 当进程的控制终端的用户按下中断正在运行的进程的关键SIGINT被发送。

SIGHUP 终止进程 终端线路挂断
UNIX中进程组织结构为 session (会话)包含一个前台进程组及一个或多个后台进程组,一个进程组包含多个进程。一个session可能会有一个session首进程,而一个session首进程可能会有一个控制终端。一个进程组可能会有一个进程组首进程。进程组首进程的进程ID与该进程组ID相等。这儿是可能会有,在一定情况之下是没有的。与终端交互的进程是前台进程,否则便是后台进程。

SIGPIPE:Broken pipe:向一个没有读端的管道写数据。默认动作为终止进程。

#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

5,set_new_handler函数

    std::set_new_handler(new_handler_terminate);
    return true;

set_new_handler函数定义如下
Typedef void (*new_handler)();
new_handler set_new_handler(new_handler new_p) throw();//C++98
new_handler set_new_handler (new_handler new_p) noexcept;//C++11
函数说明
(1)set_new_handler函数的作用是设置new_p指向的函数为new操作或new[]操作失败时调用的处理函数。

(2) 设置的处理函数可以尝试使更多空间变为可分配状态,这样新一次的new操作就可能成功。当且仅当该函数成功获得更多可用空间它才会返回;否则它将抛出bad_alloc异常(或者继承该异常的子类)或者终止程序(例如调用abort或exit)。

(3) 如果设置的处理函数返回了(例如,该函数成功获得了更多的可用空间),它可能将被反复调用,直到内存分配成功,或者它不再返回,或者被其它函数所替代。

(4)在尚未用set_new_handler设置处理函数,或者设置的处理函数为空时,将调用默认的处理函数,该函数在内存分配失败时抛出bad_alloc异常。
参考:http://blog.csdn.net/wzxq123/article/details/51502356

new_handler_terminate函数定义如下,不抛std::bad-alloc异常,直接终止进程,从而避免可能的数据冲突。

[[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();
};
发布了30 篇原创文章 · 获赞 74 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/ruiyiin/article/details/104633296