MooseFS Master main 函数代码简单学习

MFS Master

版本 2.0.x,开发语言 C,使用 AutoTools 作为构建工具集

亮点:
为 master、chunk、metalog 提供了一套统一的公用的 main 函数处理框架

暗点:
源码中不带单元测试或集成测试,缺少注释、文档
使用了比较落后的 IO 复用模式(poll)
作者编写代码不是很用心,在一些代码的逻辑处理上比较随意。另外还存在制表符、空格缩进混用的现象

目录结构

mfscommon/ 公用模块目录

mfsmaster/ mfs master 代码

main 处理框架

从 mfsmaster/Makefile.am 构建配置文件可知,入口点是在 mfscommon/main.c 中
mfschunk、mfsmetalogger 程序也使用了同一套 main 函数处理框架
不同之处在于具有各自的 init.h,在 RunTable 数组定义了不同的初始化函数

/* Run Tab */
typedef int (*runfn)(void);
struct {
    runfn fn;
    char *name;
} RunTab[]={
    {changelog_init,"change log"},
    {rnd_init,"random generator"},
    {missing_log_init,"missing chunks/files log"}, // has to be before 'fs_init'
    {dcm_init,"data cache manager"}, // has to be before 'fs_init' and 'matoclserv_init'
    {exports_init,"exports manager"},
    {topology_init,"net topology module"},
    {meta_init,"metadata manager"},
    {chartsdata_init,"charts module"},
    {matomlserv_init,"communication with metalogger"},
    {matocsserv_init,"communication with chunkserver"},
    {matoclserv_init,"communication with clients"},
    {(runfn)0,"****"}
},LateRunTab[]={
    {(runfn)0,"****"}
};

main ( )

大致流程:

  • 各种初始化
  • 进入 mainloop 死循环
  • 执行一些清理工作,程序退出
int main(int argc,char **argv) {



    strerr_init();    // 初始化错误消息散列表
    mycrc32_init();   // 初始化 crc ???


    // 打开配置文件
    cfgfile=strdup(ETC_PATH "/mfs/" STR(APPNAME) ".cfg");
    passert(cfgfile);
    if ((fd = open(cfgfile,O_RDONLY))<0 && errno==ENOENT) {


    // 分析命令行参数
    while ((ch = getopt(argc, argv, "nuvfdc:t:h?" MODULE_OPTIONS_GETOPT)) != -1) {


    // 变成后台守护进程
    if (runmode==RM_START || runmode==RM_RESTART || runmode==RM_TRY_RESTART) {
        if (rundaemon) {
            makedaemon();
        } else {
            set_signal_handlers(0);
        }
    }


    // 加载配置文件
    if (cfg_load(cfgfile,logundefined)==0) {


    // 初始化日志
    if (rundaemon) {
        if (logappname[0]) {
            openlog(logappname, LOG_PID | LOG_NDELAY , LOG_DAEMON);


    // 设置资源限制
    if (runmode==RM_START || runmode==RM_RESTART || runmode==RM_TRY_RESTART) {
        rls.rlim_cur = MFSMAXFILES;
        rls.rlim_max = MFSMAXFILES;

        nicelevel = cfg_getint32("NICE_LEVEL",-19);
        setpriority(PRIO_PROCESS,getpid(),nicelevel);
    }


    // 设置用户组、工作目录、文件掩码
    changeugid();

    if (chdir(wrkdir)<0) {

    umask(cfg_getuint32("FILE_UMASK",027)&077);


    // 初始化文件锁
    ch = wdlock(runmode,locktimeout);


    // 初始化程序模块
    fprintf(stderr,"initializing %s modules ...\n",logappname);

    if (initialize()) {           // 执行 RunTable 数组定义的函数
        if (initialize_late()) {  // 执行 LateRunTab 数组定义的函数

            // 进入主循环
            mainloop();


    // 程序退出清理
    mfs_syslog(LOG_NOTICE,"exititng ...");
    destruct();
    free_all_registered_entries();
    signal_cleanup();
    cfg_term();
    strerr_term();
    closelog();
    free(logappname);
    wdunlock();
    mfs_arg_syslog(LOG_NOTICE,"process exited successfully (status:%d)",ch);
    return ch;
}

mainloop()

void mainloop() {

    pollentry *pollit;    // pollhead 函数链表   main_poll_register()
    eloopentry *eloopit;  // eloophead 函数链表  main_eachloop_register()
    timeentry *timeit;    // timehead 函数链表
    ceentry *ceit;        // cehead (can exit,退出的第二阶段)
    weentry *weit;        // wehead (want exit,退出的第一阶段)
    rlentry *rlit;        // rlhead
    inentry *init;        // inhead (info,打印额外的日志)


    /*
    表示收到 temination 退出信号后,要执行的几个阶段
    t=0:正常死循环
     --> 收到退出信号
     --> t=1:执行 want exit 函数
     --> t=2:执行 can exit 函数
     --> t=3:退出死循环
    */
    t = 0;
    /*
    表示收到 reload 重新加载信号后的几种处理方式
    r=0 不重载
    r=1 重新加载配置文件,cfg_reload()
    r=2 父进程 waitpid 已退出的子进程,waitpid(-1,&status,WNOHANG)
    r=3 打印额外日志信息,执行 inhead 函数链表
    */
    r = 0;


    while (t!=3) {
        pdesc[0].fd = signalpipe[0];   // 数组第0个文件描述符用于监听内部事件

        // 执行 pollhead 函数链表的注册函数,向 pdesc 数组注册要监听的文件描述符和事件
        for (pollit = pollhead ; pollit != NULL ; pollit = pollit->next) {
            pollit->desc(pdesc,&ndesc);
        }

        // poll 阻塞,等待描述符就绪或超时
        // 超时为 10 毫秒,实现定时执行
        i = poll(pdesc,ndesc,10);


        } else {
            // 内部事件处理
            if ((pdesc[0].revents)&POLLIN) {
                uint8_t sigid;
                if (read(signalpipe[0],&sigid,1)==1) {
                    if (sigid=='\001' && t==0) {
                }
            }

            // 调用 pollhead 链表的处理函数
            for (pollit = pollhead ; pollit != NULL ; pollit = pollit->next) {
                pollit->serve(pdesc);
            }
        }


        // event loop 函数链表
        // mfs master 并没有注册任何函数
        for (eloopit = eloophead ; eloopit != NULL ; eloopit = eloopit->next) {
            eloopit->fun();
        }


        // 等子进程
        if (r==2) {
            while ( (pid = waitpid(-1,&status,WNOHANG)) >= 0) {


        // 遍历 timehead 链表,查看当前时间,判断是否应该执行定时任务
        for (timeit = timehead ; timeit != NULL ; timeit = timeit->next) {
            if (usecnow >= timeit->nextevent) {


                    timeit->fun();



        if (t==0) {
            // reload 重新加载配置文件
            if (r==1) {
                cfg_reload();
                for (rlit = rlhead ; rlit!=NULL ; rlit=rlit->next ) {
                    rlit->fun();
                }
                r = 0;

            // info 打印日志
            } else if (r==3) {
                for (init = inhead ; init!=NULL ; init=init->next ) {
                    init->fun();
                }
                r = 0;
            }
        }



        // want exit 退出
        if (t==1) {
            for (weit = wehead ; weit!=NULL ; weit=weit->next ) {
                weit->fun();
            }
            t = 2;
        }


        // can exit 退出
        if (t==2) {
            i = 1;
            for (ceit = cehead ; ceit!=NULL && i ; ceit=ceit->next ) {
                if (ceit->fun()==0) {
                    i=0;
                }
            }
            if (i) {
                t = 3;
            }
        }



    }


}

初始化函数

列表见 init.h RunTable 数组

    {changelog_init,"change log"},
    {rnd_init,"random generator"},
    {missing_log_init,"missing chunks/files log"}, // has to be before 'fs_init'
    {dcm_init,"data cache manager"}, // has to be before 'fs_init' and 'matoclserv_init'
    {exports_init,"exports manager"},
    {topology_init,"net topology module"},
    {meta_init,"metadata manager"},
    {chartsdata_init,"charts module"},
    {matomlserv_init,"communication with metalogger"},
    {matocsserv_init,"communication with chunkserver"},
    {matoclserv_init,"communication with clients"},

changelog_init() 初始化元数据日志的配置

    // 元数据日志,默认 50 个
    // # number of metadata change log files (default is 50)
    BackLogsNumber = cfg_getuint32("BACK_LOGS",50);


    // 内存中保留日志量,默认 10 分钟
    // # how many seconds of change logs have to be preserved in memory (default is 1800; this sets the minimum, actual number may be a bit bigger 
    // # due to logs being kept in 5k blocks; zero disables extra logs storage)
    ChangelogSecondsToRemember = cfg_getuint16("CHANGELOG_PRESERVE_SECONDS",600);



    // 注册重新加载配置文件要调用的函数
    main_reload_register(changelog_reload);


    // 当前日志文件描述符
    currentfd = NULL;

meta_init() 初始化元数据

    if (fs_strinit()<0) {


    if (chunk_strinit()<0) {


    if (xattr_init()<0) {


    if (posix_acl_init()<0) {


    if (csdb_init()<0) {


    if (sessions_init()<0) {


    if (of_init()<0) {


    if (meta_loadall()<0) {


    meta_reload();
    main_reload_register(meta_reload);
    main_time_register(3600,0,meta_dostoreall);
    main_destruct_register(meta_term);
    fs_renumerate_edge_test();

meta_loadall()

    // 自动修复(应用 changelog 恢复 metadata)
    // 此变量由命令行的参数指定 -a : automatically restore metadata from change logs
    if (allowautorestore) {

        // 遍历目录,找 metadata 开头的文件,找出版本号为最新的文件
        dd = opendir(".");
            while ((dp = readdir(dd)) != NULL) {
                if (strlen(dp->d_name)>8 && memcmp(dp->d_name,"metadata",8)==0) {
                    status = meta_check_metadatafile(dp->d_name,&ver,&fileid);

                        if (ver>bestver) {



        // 遍历几个目录,找出版本号最新的 metadata.mfs.emergency 文件
        if (bestfileid!=0) { // use emergency locations only if valid fileid has been found
            hfname = meta_create_homedir_emergency_filename();

            while ((fname = meta_emergency_locations[i++])!=NULL) {

                if (status==META_CHECK_OK && ver>bestver && fileid==bestfileid) {
                    bestver = ver;


                    bestfname = strdup(fname);


        // 加载最新的 metadata 文件,同后面 else 部分
        if (meta_loadfile(bestfname)<0) {


        // 应用 changelog 日志

        // 遍历目录
        dd = opendir(".");

                    while ((dp = readdir(dd)) != NULL) {

                    // filenames 数组保存 changelog 文件名,其中的 changelog 文件要符合 最后一行版本号 >= metadata 的版本号
                    filenames[pos] = strdup(dp->d_name);
                    firstlv = changelog_findfirstversion(filenames[pos]);  // 读取第一行记录的版本号
                    lastlv = changelog_findlastversion(filenames[pos]);    // 这段代码逻辑不清,猜测为读取最后一行记录的版本号

                        // 获得 changelog 最新版本号
                        if (lastlv > maxlastlv) {
                            maxlastlv = lastlv;



            // 将 changelog 排序保存到一个数组
            merger_start(files,filenames,MAXIDHOLE,bestver,maxlastlv);


           // 应用日志
            if (merger_loop(verboselevel)!=0) {



        // 将 "metadata.mfs" 改名为 "metadata.mfs.XXXXXX" ???

    } else {
        // 读 metadata.mfs 文件头的版本等信息,判断文件是否合法
        switch (meta_check_metadatafile("metadata.mfs",&ver,&fileid)) {

        // 判断 metadata.mfs.back 是否合法,并和 metadata.mfs 比较哪个版本更新
        if (meta_check_metadatafile("metadata.mfs.back",&bestver,&bestfileid)==META_CHECK_OK && bestver>ver && bestfileid!=0 && fileid!=0 && bestfileid!=fileid) {



        /*
         meta_loadfile 里面打开 metadata.mfs 判断一下版本号
         如果是 "MFSM NEW",表示新建一个 metadata
         其它则调用 meta_load(),这里没看懂,需要了解 fs 具体的数据结构!!!
        */
        if (meta_loadfile("metadata.mfs")<0) {

        // 文件改名           为什么要改名??? 
        // 注:程序正常退出时调用了 meta_term()恢复此文件名
        if (rename("metadata.mfs","metadata.mfs.back")<0) {

meta_check_metadatafile() 检查 metadata 版本号

名称 字节 注释
标识和程序版本号 8 格式为 “MFSM x.y”,比如 “MFSM 2.0”
如果为 “MFSM NEW” 说明是新创建的 metadata 文件
元数据版本、时间戳 16 版本 < 2.0,4 到 12 字节为 metaversion
版本 >= 2.0,前 8 字节为 metaversion,后 8 字节为 metafileid

metaversion 从 1 开始编号,文件操作导致数值递增 meta_version_inc(),和 changlog 中的记录的版本号对应
metafileid 等于 main_keep_alive() 取出的时间(秒) + 一个随机数
其它 metadata
结束符 16 版本 < 1.6,为 16 个’\0’
版本 >= 1.6,为 “[MFS EOF MARKER]”
# head -c 32 /opt/lib/mfs/metadata.mfs.back | hexdump -C 
00000000  4d 46 53 4d 20 32 2e 30  00 00 00 0b d2 f1 0e f6  |MFSM 2.0........|
00000010  54 21 08 bd a2 ab 63 a7  53 45 53 53 20 31 2e 32  |T!....c.SESS 1.2|
00000020

# tail -c 32 /opt/lib/mfs/metadata.mfs.back | hexdump -C 
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000010  5b 4d 46 53 20 45 4f 46  20 4d 41 52 4b 45 52 5d  |[MFS EOF MARKER]|
00000020

changelog

# head -n3 /opt/lib/mfs/changelog.0.mfs
50799771430: 1474617600|FREEINODES():2
50799771431: 1474617600|LENGTH(794995,53303806464)
50799771432: 1474617600|UNLOCK(245006645)

-eof-

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章