对于AppInit中剩下的一部分代码,我们首先浏览一下主要实现的功能,然后再具体介绍每个函数的实现方法。
// src/bitcoind.cpp line 127-185
// -server defaults to true for bitcoind but not for the GUI so do this here
gArgs.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
InitLogging();
InitParameterInteraction();
if (!AppInitBasicSetup())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (!AppInitParameterInteraction())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (!AppInitSanityChecks())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (gArgs.GetBoolArg("-daemon", false))
{
#if HAVE_DECL_DAEMON
fprintf(stdout, "Bitcoin server starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
#else
fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
return false;
#endif // HAVE_DECL_DAEMON
}
// Lock data directory after daemonization
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
exit(EXIT_FAILURE);
}
fRet = AppInitMain(threadGroup, scheduler);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
} catch (...) {
PrintExceptionContinue(nullptr, "AppInit()");
}
if (!fRet)
{
Interrupt(threadGroup);
threadGroup.join_all();
} else {
WaitForShutdown(&threadGroup);
}
Shutdown();
return fRet;
首先通过SoftSetBoolArg()
设置了-server
参数为true
,SoftSetBoolArg()
首先判断参数是否已经设置过了,如果是,返回false
;否则就设置对应的值,返回true
。而-server
参数表示是否接收RPC命令,这里因为是bitcoind,默认作为核心服务器接收bitcoin-cli
以及bitcoin-tx
传送的命令。
接下来包括下面几个函数:
这几个函数将在后面分章节详细介绍,先介绍这部分代码中的其他部分。在AppInitSanityChecks()
之后,程序获取了-daemon
参数,如果设置了这个参数,表示bitcoind运行后将以守护进程(后台进程)的方式运行,其中daemon()
函数的参数描述如下,
链接: http://man7.org/linux/man-pages/man3/daemon.3.html
The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons. If nochdir is zero, daemon() changes the process's current working directory to the root directory ("/"); otherwise, the current working directory is left unchanged. If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made to these file descriptors.
意思是说daemon()
可以将当前进程脱离终端的控制,并转为系统后台进程,函数传入两个参数,第一个是nochdir
,为0表示将工作目录改为系统根目录/
;为1表示将当前路径设为工作目录。第二个参数noclose
为0表示重定向stdin、stdout、stderr到/dev/null
,即不显示任何信息;为1表示不改变这些文件描述符。
进程后台化之后,通过AppInitLockDataDirectory()
来锁定数据目录,防止程序运行期间随意修改数据目录中的内容。在AppInitMain()
结束之后,如果返回值fRet
为false
,那么强制结束所有线程;否则就等待所有线程运行结束。最后通过ShutDown()
完成清理工作。
InitLogging
的实现位于src/util.cpp
中,
// src/util.cpp line 807-816
void InitLogging()
{
fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Bitcoin version %s\n", FormatFullVersion());
}
首先从命令行参数当中获取三个参数,
-printtoconsole
:将所有输出信息都直接输出到终端,而不是默认的debug.log
文件。-logtimestamps
:给每一条输出信息附带时间戳,默认值为附带。-logtimemicros
:让时间戳精确到微秒精度,默认不附加。-logips
:输出信息中附加ip地址,默认不附加。再来看看LogPrintf()
,它调用了LogPrintStr()
来输出信息,
// src/util.cpp line 328-366
int LogPrintStr(const std::string &str)
{
int ret = 0; // Returns total number of characters written
static std::atomic_bool fStartedNewLine(true);
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
if (fPrintToConsole)
{
// print to console
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
fflush(stdout);
}
else if (fPrintToDebugLog)
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
// buffer if we haven't opened the log yet
if (fileout == nullptr) {
assert(vMsgsBeforeOpenLog);
ret = strTimestamped.length();
vMsgsBeforeOpenLog->push_back(strTimestamped);
}
else
{
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
fs::path pathDebug = GetDataDir() / "debug.log";
if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
setbuf(fileout, nullptr); // unbuffered
}
ret = FileWriteStr(strTimestamped, fileout);
}
}
return ret;
}
此函数首先判断判断fPrintToConsole
是否为true
,是的话就直接输出信息到终端;否则再判断fPrintToDebugLog
是否为true
,是的话就输出信息到debug.log
文件中;如果两个都没有定义,那么就不输出任何调试信息。