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 ,赋值 THIS ( global_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_size ( 8M )大小的内存映射区域
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_SIZE ( 16384 )个事件池,创建过程如下:
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_PROCESS ( server ), 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/ 下的 vols , peers 等文件夹,创建监听端
口,设置事件处理函数,恢复上次 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);
// 创建 vols , peers 等工作目录
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/ 目录获取获取以前操作的 peer , volume , bricks 等
信息,保存到结构体中。
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 时候注册的事件处理函数处理事件。
通过 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 ,赋值 THIS ( global_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_size ( 8M )大小的内存映射区域
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_SIZE ( 16384 )个事件池,创建过程如下:
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_PROCESS ( server ), 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/ 下的 vols , peers 等文件夹,创建监听端
口,设置事件处理函数,恢复上次 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);
// 创建 vols , peers 等工作目录
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/ 目录获取获取以前操作的 peer , volume , bricks 等
信息,保存到结构体中。
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 时候注册的事件处理函数处理事件。