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-

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