glusterfs代碼框架分析02

2 GlusterFS damon
通過 ps -ef 查看進程項,可以發現 damon 啓動項詳細情況爲: /usr/local/sbin/glusterd -p
/var/run/glusterd.pid
。即可執行文件名稱爲 glusterd。僅一個參數-p
該服務啓動入口處爲 glusterfsd/src/glusterfsd.c main 函數,該入口也是 server client
的啓動入口。下面對啓動代碼分析一下。
1. ret = glusterfs_globals_init ();
該函數主要初始化全局變量,裏面主要包含了下面幾個初始化函數:
1.1 ret = glusterfs_ctx_init ();
//
該函數創建進程上下文 glusterfs_ctx 變量並初始化。
1.2 ret = glusterfs_this_init ();
//
該函數初始化 global_xlator,並將上面創建的 glusterfs_ctx 賦值給 global_xlator
THIS”目前指向該 translator
1.3 gf_mem_acct_enable_set ()
//
該函數讀取參數或環境變量賦值全局變量 gf_mem_acct_enable
2. ctx = glusterfs_ctx_get ();
//
獲取上面創建的 lusterfs_ctx,無需解釋。
3. ret = glusterfs_ctx_defaults_init (ctx);
//
初始化 ctx 衆多參數
3.1 xlator_mem_acct_init (THIS, gfd_mt_end);
//
根據 1.3,賦值 THISglobal_xlator)的 mem_acct 變量。
其中
#define THIS (*__glusterfs_this_location())。該宏通過__glusterfs_this_location()
裏的函數“
this_location = pthread_getspecific (this_xlator_key);”獲取線程私有數據
this_xlator_key 的值。獲取過程無需解釋。
3.2 ctx->process_uuid = generate_uuid ();
//
初始化 ctx->process_uuid 爲由時間和主機等組成字符串。
3.3 ctx->page_size = 128 * GF_UNIT_KB;
ctx->iobuf_pool = iobuf_pool_new (8 * GF_UNIT_MB, ctx->page_size);
//
創建一個總大小爲 8 兆大小,每頁 128 個字節的 io 內存池。 創建過程如下:
3.3.1 iobuf_pool = GF_CALLOC (sizeof (*iobuf_pool), 1,gf_common_mt_iobuf_pool);
INIT_LIST_HEAD (&iobuf_pool->arenas.list);
INIT_LIST_HEAD (&iobuf_pool->filled.list);
INIT_LIST_HEAD (&iobuf_pool->purge.list);
iobuf_pool->arena_size = arena_size;
iobuf_pool->page_size = page_size;
//
創建一個 iobuf_pool 結構體並初始化結構體變量,三個存儲類型列表,和總
內存池大小以及單頁大小等。
3.3.2 iobuf_pool_add_arena (iobuf_pool);
//
繼續深入初始化 iobuf_pool,詳情如下:
3.3.2.1 iobuf_arena = __iobuf_pool_add_arena (iobuf_pool);
//
繼續深入初始化 iobuf_pool,詳情如下:
3.3.2.1.1 iobuf_arena = __iobuf_arena_unprune (iobuf_pool);
//
嘗試從 iobuf_pool->purge.list 列表中回收 iobuf_arena
3.3.2.1.2 iobuf_arena = __iobuf_arena_alloc (iobuf_pool);
//
如果上步回收失敗則創建一個 obuf_arena,創建過程如下:
3.3.2.1.2.1 iobuf_arena = GF_CALLOC (sizeof (*iobuf_arena), 1,
gf_common_mt_iobuf_arena);
//
創建一個 iobuf_arena 結構體
3.3.1.1.2.2 INIT_LIST_HEAD (&iobuf_arena->list);
INIT_LIST_HEAD (&iobuf_arena->active.list);
INIT_LIST_HEAD (&iobuf_arena->passive.list);
//
初始化 iobuf_arena list
3.3.1.1.2.3 arena_size = iobuf_pool->arena_size;
iobuf_arena->mem_base = mmap (NULL, arena_size,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
//
申請一快 arena_size8M)大小的內存映射區域
3.3.1.1.2.4 __iobuf_arena_init_iobufs (iobuf_arena);
//
對該內存區域進行配置分頁,詳情如下:
3.3.1.1.2.4.1 arena_size = iobuf_arena->iobuf_pool->arena_size;
page_size = iobuf_arena->iobuf_pool->page_size;
iobuf_cnt = arena_size / page_size;
//
根據區域大小和一頁大小計算可以分多少頁
3.3.1.1.2.4.2 iobuf_arena->iobufs = GF_CALLOC (sizeof (*iobuf),
iobuf_cnt,gf_common_mt_iobuf);
//
申請 iobuf_cnt iobuf 用來存儲分頁信息。
3.3.1.1.2.4.3 iobuf = iobuf_arena->iobufs
//
獲取內存區域的首地址爲一個頁信息的指針
for (i = 0; i < iobuf_cnt; i++)
INIT_LIST_HEAD (&iobuf->list);

//初始化 iobuf->list
LOCK_INIT (&iobuf->lock);//
加鎖
iobuf->iobuf_arena = iobuf_arena;
//
iobuf 所屬於 iobuf_arena
iobuf->ptr = iobuf_arena->mem_base + offset;
//
iobuf_arena->mem_base 分出
page_size 區域給 iobuf->ptr
list_add (&iobuf->list, &iobuf_arena->passive.list);
//
將該頁填入 obuf_arena->passive列表,(未使用)
iobuf_arena->passive_cnt++;
//
計數器加加
offset += page_size;
//
重新計算分出頁面首地址的偏移量
iobuf++;
//
iobuf_arena->iobufs 獲取下個 iobuf 的首指針
}
3.3.1.1.2.5 iobuf_pool->arena_cnt++;
//
區域計數器加加
3.3.2.1.3 list_add_tail (&iobuf_arena->list, &iobuf_pool->arenas.list);
//
將回收或創建的 iobuf_arena 添加到 iobuf_pool->arenas 列表中。
3.4 ctx->event_pool = event_pool_new (DEFAULT_EVENT_POOL_SIZE);
//
創建 DEFAULT_EVENT_POOL_SIZE16384)個事件池,創建過程如下:
3.4.1 event_pool = event_ops_epoll.new (count);
//
調用 event_ops_epoll.new 函數創建 event_pool,詳情如下:
3.4.1.1 event_pool = GF_CALLOC (1, sizeof (*event_pool),
gf_common_mt_event_pool);
event_pool->count = count;
event_pool->reg = GF_CALLOC (event_pool->count,
sizeof (*event_pool->reg),
gf_common_mt_reg);
//
創建一個 event_pool 結構體並初始化其變量,並創建
count event_pool->reg 結構體
3.4.1.2 epfd = epoll_create (count);
event_pool->fd = epfd;
event_pool->count = count;
創建 epoll 變量,並將創建句柄賦值給 event_pool->fd,用來監聽
3.4.1.3 pthread_mutex_init (&event_pool->mutex, NULL);
pthread_cond_init (&event_pool->cond, NULL);
//
初始化線程鎖和條件鎖,完成 event_pool 初始化
3.4.2 event_pool->ops = &event_ops_epoll;
//
函數指針結構體賦值操作, 以後該 event_pool->ops 的對應的函數指針
均對應於
event_ops_epoll 結構體重的函數指針。
3.4.3 在創建監聽端口時候,會調用 event_ops_register()註冊事件處理函數。
3.5 pool = GF_CALLOC (1, sizeof (call_pool_t),gfd_mt_call_pool_t);
pool->frame_mem_pool = mem_pool_new (call_frame_t, 16384);
pool->stack_mem_pool = mem_pool_new (call_stack_t, 8192);
ctx->stub_mem_pool = mem_pool_new (call_stub_t, 1024);
//
調用 mem_pool_new 函數創建相應類型的內存池 n 個, 該函數爲一函數宏:
//#define mem_pool_new(type,count) mem_pool_new_fn (sizeof(type), count),創
//建過程如下:
3.5.1 padded_sizeof_type = sizeof_type + GF_MEM_POOL_PAD_BOUNDARY;
//
一個 type 類型的結構體所佔內存(令額外加上鍊表結構體指針)
3.5.2 mem_pool = GF_CALLOC (sizeof (*mem_pool),
1, gf_common_mt_mem_pool);
mem_pool->padded_sizeof_type = padded_sizeof_type;
mem_pool->cold_count = count;
mem_pool->real_sizeof_type = sizeof_type;
//
創建並初始化一個 mem_pool 結構體
3.5.3 pool = GF_CALLOC (count, padded_sizeof_type, gf_common_mt_long);
//
創建一個大內存池,大小爲 count*padded_sizeof_type
3.5.4 for (i = 0; i < count; i++) {
list = pool + (i * (padded_sizeof_type));
INIT_LIST_HEAD (list);
list_add_tail (list, &mem_pool->list);
}
//
將該打內存池以 padded_sizeof_type 等分,並用 list_head *list 結構體將
等分串到
mem_pool->list 變量上。(即每等份內存除包含 type 結構體大小
內存外海需要包含
GF_MEM_POOL_PAD_BOUNDARY 大小的鏈表結構,對
3.5.1
3.6 cmd_args = &ctx->cmd_args;
cmd_args->log_level = DEFAULT_LOG_LEVEL;
cmd_args->fuse_direct_io_mode = GF_OPTION_DISABLE;
//
設置缺省的明兩行參數信息,如 log 等級, fuse 模式等。
3.7 lim.rlim_cur = RLIM_INFINITY;
lim.rlim_max = RLIM_INFINITY;
setrlimit (RLIMIT_CORE, &lim);
//
設置進程資源限制,可以 google 一下。
4. ret = parse_cmdline (argc, argv, ctx);
//
解析命令行參數
4.1 if (ENABLE_DEBUG_MODE == cmd_args->debug_mode)
//debug
模式則重新定向 log 的等級和輸出。
4.2 process_mode = gf_get_process_mode (argv[0]);
//
根據可執行程序的名稱來判定該應用程序的工作模式。 並設置相關卷文件路徑該:
cmd_args->volfile = gf_strdup (DEFAULT_CLIENT_VOLFILE)。該 damon 模式爲
GF_GLUSTERD_PROCESS,還有 GF_SERVER_PROCESSserver), GF_CLIENT_PROCESS
client
5. ret = logging_init (ctx);
//log
初始化,打開 log 文件
5.1 ret = set_log_file_path (cmd_args);
//
根據參數設置不同的 log 文件路徑和名稱
5.2 if (gf_log_init (cmd_args->log_file) == -1)
//
打開 log 文件,獲取文件句柄
6. ret = create_fuse_mount (ctx);
//
damon 由於沒有 mount point,所以會直接返回,我們將在 client 中詳細分析。
7. ret = daemonize (ctx);
//
根據是否 debug 模式等參數,來決定是否啓動新進程重新定向輸入輸入,並關閉此 shell
8. ret = glusterfs_volumes_init (ctx);
//
該函數負責創建監聽端口,與監聽端口建立連接,或通過 RPC deamon 上獲取卷配置
信息等工作。不通過的工作模式其工作流程亦不同。此
deamon 工作流程如下:
8.1 if (cmd_args->sock_file) //建立 brick 監聽端口
if (cmd_args->volfile_server)//damon 監聽端口建立連接
//damon 命令行不含該參數,所以不會執行兩處 if 語句。
8.2 fp = get_volfp (ctx);
//
打開卷配置文件,獲取文件操作句柄,此卷文件爲:
/usr/local/etc/glusterfs/glusterd.vol, 參考 4.2
8.3 ret = glusterfs_process_volfp (ctx, fp);
//
對卷文件進行解析,並執行該卷信息所對應的 translator 的操作。其詳細過程如
下:
8.3.1 graph = glusterfs_graph_construct (fp);
//
利語法分析器 yyparse()(google search)函數用解析卷配置文件構建一個
graph 結構體。 在解析過程中調用 xlator_set_type 已經將各個 translator 對應
的動態庫打開,並獲取了相關函數和結構體的指針。 參考:
8.3.2.1.2
libglusterfs/src/graph.y 文件。
8.3.2 ret = glusterfs_graph_prepare (graph, ctx);
//
對構建的 graph 結構預處理一下,其內部處理情況爲:
8.3.2.1 ret = glusterfs_graph_settop (graph, ctx);
//
設置 graph top translator,默認卷配置文件中的最後一個 translator
8.3.2.1 ret = glusterfs_graph_readonly (graph, ctx);
ret = glusterfs_graph_acl (graph, ctx);
ret = glusterfs_graph_mac_compat (graph, ctx);
//
根據 cmd_args 參數信息調用 glusterfs_graph_insert()函數來決定是
否額外添加一個
translator,並設置爲 raph top。其添加過程如下:
8.3.2.1.1 ixl = GF_CALLOC (1, sizeof (*ixl), gf_common_mt_xlator_t);
ixl->ctx = ctx;
ixl->graph = graph;
ixl->options = get_new_dict ();
ixl->name = gf_strdup (name);
//
創建一個 translator 結構體,並初始化
8.3.2.1.2 if (xlator_set_type (ixl, type) == -1)
//
設置 translator 的類型,並根據類型調用 xlator_dynload
入相應動態庫和函數,其載入細節如下:
8.3.2.1.2.1 handle = dlopen (name,
RTLD_NOW|RTLD_GLOBAL);
xl->dlhandle = handle;
//
調用 dlopen 打開動態庫句柄,並賦值。
8.3.2.1.2.2 if (!(xl->fops = dlsym (handle, "fops")))
if (!(xl->cbks = dlsym (handle, "cbks")))
if (!(xl->init = dlsym (handle, "init")))
if (!(vol_opt->given_opt = dlsym (handle,
"options")))
//
利用 dlsym 打開動態庫中庫函數,獲取函數
指針
8.3.2.1.2.3 fill_defaults (xl);
//
設置 Xl translator 的其他未設置的選項爲默
認選項
8.3.2.1.3 if (glusterfs_xlator_link (ixl, graph->top) == -1)
//
將新創建的 translator graph->top 建立父子關係。建立
過程如下:
8.3.2.1.3.1 xlparent = (void *) GF_CALLOC (1, siz
gf_common_mt_xlator_list_t);
xlchild = (void *) GF_CALLOC (1, sizeof (*xlchild),
gf_common_mt_xlator_list_t);
//
創建一個 parent 和一個 child
8.3.2.1.3.2 xlparent->xlator = pxl;
for (tmp = &cxl->parents; *tmp; tmp = &(*tmp)->next);
*tmp = xlparent;
//
賦值,並將 graph->top 的兄弟的 parents 指針指向
xlparent
8.3.2.1.3.3 xlchild->xlator = cxl;
for (tmp = &pxl->children; *tmp; tmp = &(*tmp)->next);
*tmp = xlchild;
//
賦值,並將新創建 translator 的兄弟的 child 指針指向
Xlchild,完成父子關係的建立
8.3.2.1.4 glusterfs_graph_set_first (graph, ixl);
graph->top = ixl;
//
將新添加的 translator 設置爲 graph first top,完成。
8.3.3 ret = glusterfs_graph_activate (graph, ctx);
//
初始化 graph 中的各個 translator,創建 socket,建立事件監聽函數。
其詳細過程如下:
8.3.3.1 ret = glusterfs_graph_validate_options (graph);
//
驗證卷配置文件中的 options 參數的有效性。
8.3.3.2 ret = glusterfs_graph_init (graph);
//
參考 8.3.1,自上而下調用 graph 中各個 translator init ()函數初始化。
damon只會調用 mgmt init 函數(xlators/mgmt/glusterd/src/glusterd.c)。
在初始化過程中建立
/etc/glusterd/下的 volspeers 等文件夾,創建監聽端
口,設置事件處理函數,恢復上次
deamon 退出的狀態燈操作。簡要分析
如下:
8.3.3.2.1 dir_data = dict_get (this->options, "working-directory");
//
獲取工作主目錄
ret = gf_cmd_log_init (cmd_log_filename);
//
設置 CLI 命令 log 文件
ret = mkdir (voldir, 0777);
//
創建 volspeers 等工作目錄
ret = glusterd_rpcsvc_options_build (this->options);
//
設置 PRC server 的選項配置信息 options
8.3.3.2.2 rpc = rpcsvc_init (this->ctx, this->options);
//
創建一個 rpc server 結構體,並初始化一些參數信息。利用函
ret = rpcsvc_program_register (svc, &gluster_dump_prog);添加
gluster_dump_prog svc->programs 鏈表上。
8.3.3.2.3 ret = rpcsvc_register_notify (rpc, glusterd_rpcsvc_notify, this);
//
主次 rpc server 一個 notify 處理函數。將該處理函數添加到
svc->notify 列表上。
8.3.3.2.4 ret = rpcsvc_create_listeners (rpc, this->options, this->name);
//
利用 rpc 類型調用 ret = rpcsvc_create_listener (svc,
options, transport_name);
創建 listener, 創建過程如下:
8.3.3.2.4.1 trans = rpcsvc_transport_create (svc, options, name);
//
創建一個 rpc_transport_t 結構體,該結構體動態載入 soket
庫以及其函數指針,創建一個 socket,其創建過程如下:
8.3.3.2.4.1.1 trans = rpc_transport_load (svc->ctx, options, name);
//
動態載入相應類型的 RPC 庫並調用庫的 init 初始化。
8.3.3.2.4.1.2 ret = rpc_transport_listen (trans);
//
調用 sokcet 庫的 listen 建立監聽端口, 並調用
ctx->event_pool event_register_epoll 函數將 sokcet 句柄利
epoll_ctl 監聽。 詳細見參考 soket listen 函數的源碼和
event_register_epoll 函數。
8.3.3.2.4.1.3 ret = rpc_transport_register_notify (trans,
rpcsvc_notify, svc); rpcsvc_notify
//
註冊 trans notify 處理函數爲
8.3.3.2.4.2 listener = rpcsvc_listener_alloc (svc, trans);
//
創建 listener,並將該 rpc server trans 賦值給 listener
將該
listener 添加到 prc server svc->listeners 鏈表上。
8.3.3.2.5 ret = glusterd_program_register (this, rpc,
&glusterd1_mop_prog);
ret = glusterd_program_register (this, rpc, &gd_svc_cli_prog);
ret = glusterd_program_register (this, rpc, &gd_svc_mgmt_prog);
ret = glusterd_program_register (this, rpc, &gluster_pmap_prog);
ret = glusterd_program_register (this, rpc, &gluster_handshake_prog);
//
註冊 5 個事件處理結構體到 rpc->programs 列表上
8.3.3.2.6 ret = configure_syncdaemon (conf);
//a.定義 RUN_GSYNCD_CMD(prf),該函數用來 damon 啓動執行
命令
//b.配置 geosync 信息。
8.3.3.2.7 ret = glusterd_restore ();
//
/etc/glusterd/目錄獲取獲取以前操作的 peervolumebricks
信息,保存到結構體中。
8.3.3.2.8 glusterd_restart_bricks (conf);
//
根據上次 damon 運行狀態針對每個 brick 啓動一個 brick 服務
glusterd_brick_start (volinfo, brickinfo);” 該函數會調用
ret = glusterd_volume_start_glusterfs (volinfo, brickinfo);”啓動卷服
務。該函數前面大部分工作在於設置命令行參數,最後調用
ret = gf_system (cmd_str);”執行 ret = execvp (argv[0], argv)來創建新
的進程啓動服務。
最後並確定是否啓動
nfs 服務:“glusterd_check_generate_start_nfs ();
8.3.3.2.9 ret = glusterd_restart_gsyncds (conf);
//
啓動遠程同步功能
8.3.3.2 ret = glusterfs_graph_parent_up (graph);
//
調用卷配置文件中的各個 translator notify 函數,由於 ctx->master
空,所以不會執行
ret = xlator_notify (ctx->master,
GF_EVENT_GRAPH_NEW, graph);
調用 mgmt notify 函數,發送
GF_EVENT_PARENT_UP命令。該 mgmt 調用 default_notify (this, event, data);
沒有做什麼具體操作,可以忽略。
9. ret = event_dispatch (ctx->event_pool);
//
監聽事件池中註冊的句柄, 即 8.3.3.2.4.1.2 創建 soket 的句柄。 並調用事件池中註冊的
函數處理,即
event_pool->ops->event_dispatch (event_pool);。詳細過程如下:
9.1 ret = epoll_wait (event_pool->fd, event_pool->evcache,
event_pool->evcache_size, -1);
//
監聽 socket 句柄,等待事件。
9.2 ret = event_dispatch_epoll_handler (event_pool,events, i);
//
調用創建 socket 時候註冊的事件處理函數處理事件。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章