用5行代码了解EOS.IO的脉络

作者: 程序员在深圳 | 来源:发表于2018-02-01 16:38 被阅读333次
    题图

    这篇文章的目的是帮助梳理EOS.IO的核心——eosiod的代码脉络,为后续深入研究源码打下基础。

    EOS.IO项目包括好几个程序,它们的入口main()函数都在programs目录下,我列出了重要的几个程序,其中我们最关心的是eosiod,其次可能是eosio-walletdeosioc了,本文中涉及的代码基于EOS DAWN3.0

    $ pwd
    /Users/fengyajie/eos
    $ tree -L 1 programs
    programs
    ├── abi_gen   # 产生abi文件的程序
    ├── codegen  # 自动产生智能合约框架代码的程序
    ├── eosio-walletd  # 钱包程序
    ├── eosioc  # 客户端程序,通过RPC和eosiod通信
    ├── eosiod  # 核心节点程序
    ├── launcher  # 帮助启动节点的程序
    └── snapshot  # 用于创建初始块的快照程序
    

    下面来一探eosiod的究竟,打开programs/eosiod/main.cpp文件,我们可以看到main非常简单,核心代码就5行,为了方便表述,我把异常处理、版本设置和日志输出等非核心部分都省略了

    if(!app().initialize<chain_plugin, http_plugin, net_plugin>(argc, argv)) // 1
        return -1;
    initialize_logging(); // 2
    app().startup(); // 3
    app().exec(); // 4
    

    这四行代码分别起到什么作用呢?

    其中最关键的应该是第①行,它完成了3个插件的初始化工作(EOS项目里运用了大量的泛型模板编程,也灵活运用了C++1x的变长模板参数的特性,对于想学习最新C++特性的同学,这是一个很好的案例),从这一行可以看出,eosiod程序是一个插件化的框架,其中的所有功能,都是由插件实现的,你想要eosiod具备什么能力,组合不同的插件就好了。eos项目下的plugins目录含有Dawn3.0的所有插件的实现。

    plugins
    ├── account_history_api_plugin
    ├── account_history_plugin
    ├── chain_api_plugin
    ├── chain_plugin
    ├── faucet_testnet_plugin
    ├── http_plugin
    ├── mongo_db_plugin
    ├── net_api_plugin
    ├── net_plugin
    ├── producer_plugin
    ├── template_plugin
    ├── txn_test_gen_plugin
    ├── wallet_api_plugin
    └── wallet_plugin
    

    而在当前代码中,只加载了chain_pluginhttp_pluginnet_plugin,这三个目前没细看,猜想可能是区块链插件、http协议插件(与eosioc交互)以及P2P网络插件,知道了这一点,我们就可以针对性的去研究对应的实现了。

    第②行代码没有什么可说的,完成了日志的初始化工作。

    我们来看第③行,startup函数的实现也很简单

    void application::startup() {
       for (auto plugin : initialized_plugins)
          plugin->startup();
    }
    

    上面这几行代码的功能是,对每一个成功初始化的插件,调用它们的startup()函数,看startup这个词就知道,这个函数的功能是做一些初始参数的设定。

    最后来看第④行,也很简单,它的核心实现就一行代码io_serv->run()

    void application::exec() {
       // ... 其他代码,主要完成SIGINT和SIGTERM的信号处理函数的设置
    
       io_serv->run();
       // ...
    }
    

    我们来看下io_serv是什么,在libraries/appbase/include/appbase/application.hpp中,对io_serv有以下声明

    std::shared_ptr<boost::asio::io_service>  io_serv;
    

    这是一个boost库中的异步IO服务,这个服务提供一个run()函数,可以让这个程序一直运行下去,对于这点,做过服务器的同学就应该很熟悉了。

    等等,不是说5行代码吗?怎么感觉4行就已经完事儿了?这里要注意的是,还有一行代码,它不在main()函数中,它在每个插件的头文件中,用来在main()执行前,把所有的插件都注册到系统中,以http_plugin.cpp文件作为例子,就是下面这行代码

    static appbase::abstract_plugin& _http_plugin = app().register_plugin<http_plugin>();
    

    再追到register_plugin中去看看,它在application.hpp

    template<typename Plugin>
    auto& register_plugin() {
       auto existing = find_plugin<Plugin>();
       if(existing)
          return *existing;
                                              
       auto plug = new Plugin();
       plugins[plug->name()].reset(plug);
       plug->register_dependencies();
       return *plug;
    }
    

    看清楚了把,这个注册函数完成了2件事

    1. 检查插件是否注册过,注册过就直接退出,防止多次注册
    2. 如果没有注册过,就分配一个新的插件对象,然后插入到plugins中,plugins是一个map容器

    上面的map容器,和最开始第①步中的插件初始化有一定的关联,逻辑是先把所有插件注册到容器中,然后再初始化第①步中指定的插件,register_dependencies()就不展开了,它会调用不同插件的plugin_requires()实现。

    至此,我们通过5行代码,我们了解到eosiod服务运行的大致脉络,且学习到它是一个插件化服务器,同时可以推断所有的eosiod的行为来自于网络输入,即http_pluginnet_plugin两个模块,知道这些后,后续我们就可以针对性的去阅读每一个核心模块了。


    我建立了一个收费的知识星球,在这里,我会为大家营造一个沉下心来学习的环境,在今年,我至少会做三件事情:

    1. 精通比特币的实现原理

    2. 精通EOS的实现原理,并着手在EOS上建立应用

    3. 学习其他项目的白皮书,寻找有创意、有价值的项目

    以上内容会不定期的输出分享,同时也会在能力范围内解答同学的问题,期待你的加入

    相关文章

      网友评论

        本文标题:用5行代码了解EOS.IO的脉络

        本文链接:https://www.haomeiwen.com/subject/okhdzxtx.html