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-