比特币源码研读之十九

近期一直在忙我们区块链研习社代币QYB的事,从开始测试到发布已经过去2周了,一切运行正常,大家普遍反映通过使用QYB进行挖矿和转账体验,真实地感受到了比特币挖矿和比特币相关知识,比自己看文章和书籍更直观,这也是我们研习社的初衷,就是通过不同的方式让大家学习区块链知识,让大家真正通过学习来提升自己的认知,提升自己对区块链的理解。

还有我仍然坚持在我们研习社的千聊平台中开讲《比特币编程》系列课程,比特币相关的课程已达8讲,收听人数也在逐步增加,社长和听众也给了很多正面反馈,让我更有信心继续讲下去。

虽然目前事很多,但源码研读这事我是不会停的,我会继续坚持写下去,因为近期有很多朋友找到我,希望我能继续写,把源码解读完,我自己也很想继续写下去,所以,只要我有时间,一定会坚持写下去的,请各位看官监督!

下面我们正式开始第十九篇源码研读,这篇文章我们将开始AppInitMain函数源码的研读与解析。

比特币源码研读之十九_第1张图片

本文主要涉及的源码文件包括:

src/bitcond.cpp、src/init.h、src/init.cpp、src/util.h、src/util.cpp

一、daemon参数解析

在解析AppInitMain函数之前,我们先来看对daemon参数,该参数应用于比特币核心的bitcond.exe控制台程序,该程序为比特币核心的后台服务程序。daemon参数的解析代码:

if (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");

         returnfalse;

    #endif // HAVE_DECL_DAEMON

}

在该代码块中,程序首先通过GetBoolArg函数判断判断是否在启动时设置了守护进程daemon参数,如果设置了则继续执行该参数相关的代码,否则将在控制台中输出当前系统不支持守护进程的错误提示。

设置信息后其具体执行内容为:

(1)判断是否定包含了HAVE_DECL_DAEMON宏定义,该宏定义在未经编译的源码中是不包含的,需经过./configure配置后才会出现,经过configure后,其放置于src/config/bitcoin-config.h文件中,一般来说HAVE_DECL_DAEMON是默认定义的,其定义如下:

通过定义的注释我们可以看到,如果该宏定义表示的数值为1,则定义了daemon,否则不定义。一般来说该值为1。

(2)如果为1则执行fprintf(stdout, "Bitcoin server starting\n");,其表示在控制台中输出"Bitcoin server starting\n"信息,表明比特币后台守护进程在运行;

(3)通过执行daemon(1, 0)开启守护进程操作,我们看到在daemon函数中设置了两个参数,这两个参数的含义是什么呢?我们首先来看看daemon函数的定义:

#include

int daemon(int nochdir, int noclose);

参数:

当nochdir为零时,当前目录变为根目录,否则不变;

当noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信息,否则照样输出。

返回值:

deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。

从该函数的定义和参数解释我们可以看出,该函数为守护进程根据输入的参数启动该进程。我们当前设置的参数为1和0,根据其注释我们可以得知,该守护进程将当前目录设为根目录,即程序中的相对目录是从该目录开始的,第二个参数设置为0则表示不输出任何信息。

最后如果daemon()函数返回为0时则正常运行,为-1时则输出errorno对应的错误提示,并返回false,程序退出,同时我们在运行时可根据该错误进行比特币守护进程的配置。

二、AppInitMain与WaitForShutdown

此处先分析AppInitMain与WaitForShutDown的关系。我们可以看到AppInitMain返回值直接影响了WaitForShutdown的运行情况,其执行流程如图所示:

比特币源码研读之十九_第2张图片

(1)首先执行AppInitMain函数,执行相应的初始化过程;

(2)如果初始化过程运行正常,fRet则为true,否则为false;

(3)如果为true,则程序执行WaitForShutdown函数,进入程序关闭请求等待状态,我们来看下bitcoind.cpp中的WaitForShutdown函数:

     void WaitForShutdown(boost::thread_group* threadGroup)

     {

         bool fShutdown = ShutdownRequested();

        // Tell the main threads to shutdown.

        while (!fShutdown)

       {

            MilliSleep(200);

            fShutdown = ShutdownRequested();

       }

     if (threadGroup)

   {

         Interrupt(*threadGroup);

        threadGroup->join_all();

    }

}

如果fShutdown为false,则每隔200毫秒循环执行关闭请求函数ShutdownRequested(Init.cpp)的获取,直到该返回值为true时,则通知主线程执行关闭程序,其执行过程与fRet为false的代码一致,见(4);

(4)如果为false,则表明初始化失败,程序将关闭,此时将执行Interrupt函数,该函数定义于init.h,实现与init.cpp中,具体代码如下:

void Interrupt(boost::thread_group& threadGroup)

{

    InterruptHTTPServer();

   InterruptHTTPRPC();

   InterruptRPC();

   InterruptREST();

   InterruptTorControl();

  if (g_connman)

       g_connman->Interrupt();

   threadGroup.interrupt_all();

}

我们看到该代码实现HTTPServer、HTTPRPC、RPC、REST以及TorControl、connman等功能线程的中断,最后通过完成线程组的中断操作;

(5)随后通过threadGroup.join_all();实现所有中断线程的收回操作,相当于释放线程所占用的空间;

(6)最后是调用Shutdown();操作,实现程序中所有正在运行模块的关闭与停止操作。

三、小结

本研读记录主要分析了daemon参数、AppInitMain与WaitForShutDown执行过程的分析,后续将深入剖析AppInitMain的内容,因为该函数中包含了比特币核心中各模块的初始化工作,对我们了解各模块的运行过程至关重要,敬请大家期待。

比特币源码研读班班长  菜菜子

比特币编程课程

你可能感兴趣的:(比特币源码研读之十九)