接上篇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();
};