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 时候注册的事件处理函数处理事件。