比特币源码解析(11) - 可执行程序 - Bitcoind

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012183589/article/details/77898424

0x01 AppInit - Continue

对于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参数为trueSoftSetBoolArg()首先判断参数是否已经设置过了,如果是,返回false;否则就设置对应的值,返回true。而-server参数表示是否接收RPC命令,这里因为是bitcoind,默认作为核心服务器接收bitcoin-cli以及bitcoin-tx传送的命令。

接下来包括下面几个函数:

  • InitLogging():初始化日志记录以及打印方式。
  • InitParameterInteraction():初始化网络参数。
  • AppInitBasicSetup():注册相应的消息以及处理方式。
  • AppInitParameterInteraction():设置区块链运行参数,例如交易费等等。
  • AppInitSanityChecks():Sanity Check是用来检查比特币运行时所需要的所有的库是否都运行正常。
  • AppInitMain():初始化主程序。
  • ShutDown():关闭所有后台进程并清理程序。

这几个函数将在后面分章节详细介绍,先介绍这部分代码中的其他部分。在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()结束之后,如果返回值fRetfalse,那么强制结束所有线程;否则就等待所有线程运行结束。最后通过ShutDown()完成清理工作。

0x02 InitLogging

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文件中;如果两个都没有定义,那么就不输出任何调试信息。

展开阅读全文

没有更多推荐了,返回首页