Glusterfs之nfs模塊源碼分析(中)之Glusterfs實現NFS服務器


我的新浪微博:http://weibo.com/freshairbrucewoo

歡迎大家相互交流,共同提高技術。

 

五、Glusterfs實現NFS服務器

第一節、啟動過程分析

Glusterfsnfs服務器啟動命令如下:

 /usr/local/sbin/glusterfs -f /etc/glusterd/nfs/nfs-server.vol -p /etc/glusterd/nfs/run/nfs.pid 

 -l /usr/local/var/log/glusterfs/nfs.log

說明:所有列出的代碼都把錯誤處理、參數檢查和日志輸出去掉了!

上面的命令會啟動glusterfsd程序,下面是入口函數main的代碼實現:

 1 int main (int argc, char *argv[])  
 2 {  
 3     glusterfs_ctx_t  *ctx = NULL;  
 4     int               ret = -1;  
 5     ret = glusterfs_globals_init ();//初始化一些全局變量和參數  
 6     ctx = glusterfs_ctx_get ();  
 7     ret = glusterfs_ctx_defaults_init (ctx);//初始化一些glusterfs的上下文默認信息  
 8     ret = parse_cmdline (argc, argv, ctx);//解析命令行參數  
 9     ret = logging_init (ctx);//初始化日志文件  
10     gf_proc_dump_init();//初始化代表程序的全局鎖  
11     ret = create_fuse_mount (ctx);//創建fuse的主(根)xlator:mount/fuse,並且初始化相關值  
12     ret = daemonize (ctx);//設置守護進程運行模式  
13     ret = glusterfs_volumes_init (ctx);//初始化卷服務,創建相應的xlator並且初始化  
14     ret = event_dispatch (ctx->event_pool);//時間分發,將相應的事件交給相應的函數處理  
15 }  

整個main做的工作在代碼中都有注釋了,對於nfs啟動比較關心的就是兩個函數,一個是命令行參數解析函數parse_cmdline,按照上面給出的命令解析出程序啟動的需要的卷文件路徑、日志文件路徑和存放進程ID的文件。而且程序是以glusterfs模式(有三種模式:(1glusterfsd;(2glusterfs;(3glusterd)運行。

另外一個函數就是初始化具體的卷服務函數glusterfs_volumes_init,根據我們的啟動命令是啟動nfs類型的服務,每一個卷服務都會用一個xlator表示,代碼如下:

 1 int glusterfs_volumes_init (glusterfs_ctx_t *ctx)  
 2 {  
 3     FILE               *fp = NULL;  
 4     cmd_args_t         *cmd_args = NULL;  
 5     int                 ret = 0;  
 6     cmd_args = &ctx->cmd_args;  
 7     if (cmd_args->sock_file) {//是否設置了sock_file來啟動監聽服務  
 8         ret = glusterfs_listener_init (ctx);//初始化監聽服務  
 9     }  
10     fp = get_volfp (ctx);//得到描述卷的文件指針  
11     ret = glusterfs_process_volfp (ctx, fp);//處理描述卷的文件  
12 }  

從啟動命令可以看出並沒有設置cmd_args->sock_filecmd_args->volfile_server參數,所以直接進入卷處理函數glusterfs_process_volfp,下面繼續看這個函數的實現,如下:

 1  
 2 
 3 int glusterfs_process_volfp (glusterfs_ctx_t *ctx, FILE *fp)  
 4 {  
 5    glusterfs_graph_t  *graph = NULL;  
 6    int                 ret = -1;  
 7    xlator_t           *trav = NULL;  
 8    graph = glusterfs_graph_construct (fp);//根據卷描述文件構造一個graph  
 9    for (trav = graph->first; trav; trav = trav->next) {  
10     if (strcmp (trav->type, "mount/fuse") == 0) {//卷文件中不能有mount/fuse類型的卷  
11        gf_log ("glusterfsd", GF_LOG_ERROR, "fuse xlator cannot be specified " "in volume file");  
12           goto out;  
13        }  
14    }  
15    ret = glusterfs_graph_prepare (graph, ctx);//准備工作  
16   ret = glusterfs_graph_activate (graph, ctx);//激活這個圖結構的卷  
17   gf_log_volume_file (fp);//卷的日志文件  
18    ret = 0;  
19 out:  
20   if (fp)  
21    fclose (fp);  
22   if (ret && !ctx->active) {  
23     cleanup_and_exit (0);//如果沒有激活就清理掉並且直接退出整個程序  
24   }  
25   return ret;  
26 }  

繼續關注比較重要的,上面代碼中最重要的就是激活卷(graph)服務的函數glusterfs_graph_activate 了,所以繼續看這個函數的實現,代碼如下:

 1  
 2 
 3 int glusterfs_graph_activate (glusterfs_graph_t *graph, glusterfs_ctx_t *ctx)  
 4 {  
 5     int ret = 0;  
 6     ret = glusterfs_graph_validate_options (graph);//驗證所有卷包括子卷的配置選項的正確性  
 7     ret = glusterfs_graph_init (graph);//初始化由整個配置文件中的各個卷組成的圖結構  
 8     ret = glusterfs_graph_unknown_options (graph);//再次驗證是否有不知道的參數  
 9     list_add (&graph->list, &ctx->graphs);//加入到總的鏈表中進行統一管理  
10    ctx->active = graph;  
11    if (ctx->master)//附加到master(mount/fuse)節點  
12       ret = xlator_notify (ctx->master, GF_EVENT_GRAPH_NEW, graph);  
13    ret = glusterfs_graph_parent_up (graph);//設置父節點  
14   return 0;  
15 }  

graph初始化函數中有具體初始化xlator的實現,這個就關系到是怎樣連接到nfs服務器,所以繼續看這個函數的實現:

 

 1 int glusterfs_graph_init (glusterfs_graph_t *graph)  
 2 {  
 3     xlator_t           *trav = NULL;  
 4     int                 ret = -1;  
 5    trav = graph->first;//第一個節點,也就是nfs類型的節點  
 6    while (trav) {  
 7         ret = xlator_init (trav);//依次初始化每一個節點(xlator)  
 8    trav = trav->next;//指向下一個節點  
 9    }  
10   return 0;  
11 }  

繼續看初始化節點xlator的函數xlator_init ,代碼如下:

 1 int xlator_init (xlator_t *xl)  
 2 {  
 3     int32_t ret = -1;  
 4     GF_VALIDATE_OR_GOTO ("xlator", xl, out);  
 5     if (xl->mem_acct_init)  
 6         xl->mem_acct_init (xl);//如果這個函數指針不為空就調用  
 7     if (!xl->init) {//init函數指針不能為空  
 8     }  
 9     ret = __xlator_init (xl);//繼續初始化  
10     xl->init_succeeded = 1;  
11     ret = 0;  
12 out:  
13     return ret;  
14 }  

繼續看__xlator_init (xl);

 1 static int __xlator_init(xlator_t *xl)
 2 
 3 {
 4 
 5 xlator_t *old_THIS = NULL;
 6 
 7  int       ret = 0;
 8 
 9 old_THIS = THIS;
10 
11   THIS = xl;
12 
13 ret = xl->init (xl);//調用具體xlator的init函數完成初始化工作
14 
15 THIS = old_THIS;
16 
17 return ret;
18 
19 }

 

到此為止就可以看到真正調用NFSinit函數了,這個時候才真正開始執行與nfs服務相關功能的代碼。當然在結束服務的時候還會執行fini函數。

第二節、NFS協議實現的init函數

先看看這個函數的代碼吧,如下:

 1 int init (xlator_t *this) {
 2 
 3 struct nfs_state        *nfs = NULL;
 4 
 5 int                     ret = -1;
 6 
 7 if (!this)
 8 
 9   return -1;
10 
11 nfs = nfs_init_state (this);//初始化一些nfs的選項參數
12 
13 ret = nfs_add_all_initiators (nfs);//添加所有協議的初始化器
14 
15 ret = nfs_init_subvolumes (nfs, this->children);//初始化nfs的所有子卷
16 
17 ret = nfs_init_versions (nfs, this);//初始化所有nfs協議的版本
18 
19 return ret;
20 
21 }

 

上面代碼可以看出,init函數的主要作用就是初始化nfs協議的所有版本以及其所有的子卷。下面依次分析各個函數實現的功能。

1.nfs_init_state函數

這個函數代碼比較多,分步驟解析:

第一步:判斷nfs是否存在子卷,如果不存在就退出,因為必須要有子卷才能正常工作,代碼如下:

1 if ((!this->children) || (!this->children->xlator)) {
2 
3 gf_log (GF_NFS, GF_LOG_ERROR, "nfs must have at least one" " child subvolume");
4 
5 return NULL;
6 
7 }

 

第二步:為nfs_state結構體指針分配內存,分配失敗就報錯並退出程序,分配內存代碼如下:

1 nfs = GF_CALLOC (1, sizeof (*nfs), gf_nfs_mt_nfs_state);

 

第三步:啟動rpc服務:nfs->rpcsvc = nfs_rpcsvc_init (this->ctx, this->options);

第四步:根據參數nfs.mem-factor設置內存池的大小,用於提高訪問速度。

這一步首先調用函數xlator_get_volopt_info 得到參數的值,然后轉換為無符號整型並賦值給nfs->memfactor,表示內存因子,然后計算整個內存池的大小並新建這樣大小的一個內存池,代碼如下;

1 fopspoolsize = nfs->memfactor * GF_NFS_CONCURRENT_OPS_MULT;
2 
3 nfs->foppool = mem_pool_new (struct nfs_fop_local, fopspoolsize);

 

第五步:安裝第四步同樣的方式解析參數nfs.dynamic-volumesnfs.enable-ino32nfs.port,並且賦值給nfs_state結構體中相應的字段保存。

第六步:將nfs_state保存到xlator的私有數據部分並初始化nfs協議版本的鏈表。

1         this->private = (void *)nfs;
2 
3         INIT_LIST_HEAD (&nfs->versions);

 

經過上面6步這個函數就執行完了,如果其中有地方出錯都會進行相應的處理,尤其是資源的釋放處理尤為重要,保證不會發生內存泄露。其中第三步是比較重要的復雜的,涉及到rpc服務的初始化工作,所以需要詳細分析,因為后面很多操作都會依賴於rpc服務進行通信,nfs_rpcsvc_init函數定義和實現如下:

 1 rpcsvc_t * nfs_rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options)
 2 
 3 {
 4 
 5 rpcsvc_t        *svc = NULL;
 6 
 7  int             ret = -1;
 8 
 9   int             poolsize = 0;
10 
11  svc = GF_CALLOC (1, sizeof (*svc), gf_common_mt_rpcsvc_t);//分配內存資源
12 
13 pthread_mutex_init (&svc->rpclock, NULL);//初始化鎖
14 
15    INIT_LIST_HEAD (&svc->stages);//初始化rpc服務的階段處理鏈表
16 
17    INIT_LIST_HEAD (&svc->authschemes);//初始化可用的身份驗證方案鏈表
18 
19    INIT_LIST_HEAD (&svc->allprograms);//初始化存放所有程序的鏈表
20 
21   ret = nfs_rpcsvc_init_options (svc, options);//初始化一些選項信息
22 
23   ret = nfs_rpcsvc_auth_init (svc, options);//初始化權限信息
24 
25   poolsize = RPCSVC_POOLCOUNT_MULT * RPCSVC_DEFAULT_MEMFACTOR;//計算內存池大小
26 
27    svc->connpool = mem_pool_new (rpcsvc_conn_t, poolsize);//為連接對象分配內存池空間
28 
29    svc->defaultstage = nfs_rpcsvc_stage_init (svc);//初始化默認階段執行服務
30 
31  svc->options = options;//賦值選項信息
32 
33    svc->ctx = ctx;//設置屬於哪一個glusterfs的上下文
34 
35  return svc;
36 
37 }

 

這個函數是rpc服務的全局初始化函數,這是rpc服務的開始階段(rpc服務分為多個階段實現),等待rpc程序注冊的到來。整個函數其實都是在初始化一個結構體的相關內容,就是rpcsvc_t結構體,它的作用就是描述rpc服務的狀態(包括各個階段的,對rpc各個階段進行統一管理)。下面看看這個結構體的具體定義:

 1 /* Contains global state required for all the RPC services. */
 2 
 3 typedef struct rpc_svc_state {
 4 
 5         /* Contains the list of rpcsvc_stage_t list of (program, version) handlers. other options. */
 6 
 7         /* At this point, lock is not used to protect anything. Later, it'll be used for protecting stages. */
 8 
 9         pthread_mutex_t         rpclock;
10 
11         /* This is the first stage that is inited, so that any RPC based services that do not need multi-threaded 
12 
13   * support can just use the service right away. This is not added to the stages list declared later.
14 
15          * This is also the stage over which all service listeners are run. */
16 
17         rpcsvc_stage_t          *defaultstage;
18 
19         /* When we have multi-threaded RPC support, we'll use this to link to the multiple Stages.*/
20 
21         struct list_head        stages;         /* All stages */
22 
23         unsigned int            memfactor;
24 
25         struct list_head        authschemes;/* List of the authentication schemes available. */
26 
27         dict_t                  *options;/* Reference to the options */
28 
29       int                     allow_insecure;/* Allow insecure ports. */
30 
31         glusterfs_ctx_t         *ctx;
32 
33         gf_boolean_t            register_portmap;
34 
35         struct list_head        allprograms;
36 
37       struct mem_pool         *connpool;/* Mempool for incoming connection objects. */
38 
39 } rpcsvc_t;

 

nfs_rpcsvc_init函數中,有一個初始化權限信息的函數nfs_rpcsvc_auth_init 和一個初始化rpc執行階段信息的函數nfs_rpcsvc_stage_init需要重點分析。先分析權限信息的初始化函數nfs_rpcsvc_auth_init,如下:

(1)nfs_rpcsvc_auth_init函數

函數定義和實現如下:

 1 int nfs_rpcsvc_auth_init (rpcsvc_t *svc, dict_t *options)
 2 
 3 {
 4 
 5 int             ret = -1;
 6 
 7   ret = nfs_rpcsvc_auth_add_initers (svc);//增加auth-null和auth-unix兩個關鍵字代表的初始化函數
 8 
 9 ret = nfs_rpcsvc_auth_init_auths (svc, options);//開啟權限使能相關選項並且執行初始化函數
10 
11    return ret;
12 
13 }

 

這個函數主要實現增加權限的初始化函數到權限操作鏈表中,然后通過執行執行初始化函數得到一個描述相關權限信息的結構體,這個結構體包括一些操作函數指針的結構體地址和一些基本信息(如名稱)。執行初始化函數並且得到權限描述信息的實現是在如下代碼中(在nfs_rpcsvc_auth_init_auths中調用的):

 1 int nfs_rpcsvc_auth_init_auth (rpcsvc_t *svc, dict_t *options, struct rpcsvc_auth_list *authitem)
 2 
 3 {
 4 
 5 .....
 6 
 7    authitem->auth = authitem->init (svc, options);//執行權限的初始化函數
 8 
 9    authitem->enable = 1;//權限使能
10 
11 ......
12 
13 }

 

這里執行的初始化函數是在上面初始化的,有兩類權限的初始化函數,相關內容定義如下:

1auth-null

 1 rpcsvc_auth_ops_t nfs_auth_null_ops = {//權限操作相關的處理函數
 2 
 3         .conn_init              = NULL,
 4 
 5         .request_init           = nfs_auth_null_request_init,
 6 
 7         .authenticate           = nfs_auth_null_authenticate
 8 
 9 };
10 
11 rpcsvc_auth_t nfs_rpcsvc_auth_null = {//權限描述的結構體和默認值
12 
13         .authname       = "AUTH_NULL",
14 
15         .authnum        = AUTH_NULL,
16 
17         .authops        = &nfs_auth_null_ops,
18 
19         .authprivate    = NULL
20 
21 };
22 
23 rpcsvc_auth_t * nfs_rpcsvc_auth_null_init (rpcsvc_t *svc, dict_t *options)//初始化函數
24 
25 {
26 
27         return &nfs_rpcsvc_auth_null;//返回權限描述信息結構體
28 
29 }
30 
31 2)auth-unix
32 
33 rpcsvc_auth_ops_t nfs_auth_unix_ops = {//權限操作相關的處理函數
34 
35         .conn_init              = NULL,
36 
37         .request_init           = nfs_auth_unix_request_init,
38 
39         .authenticate           = nfs_auth_unix_authenticate
40 
41 };
42 
43 rpcsvc_auth_t nfs_rpcsvc_auth_unix = {//權限描述的結構體和默認值
44 
45         .authname       = "AUTH_UNIX",
46 
47         .authnum        = AUTH_UNIX,
48 
49         .authops        = &nfs_auth_unix_ops,
50 
51         .authprivate    = NULL
52 
53 };
54 
55 rpcsvc_auth_t * nfs_rpcsvc_auth_unix_init (rpcsvc_t *svc, dict_t *options)//初始化函數
56 
57 {
58 
59         return &nfs_rpcsvc_auth_unix;//返回權限描述信息結構體
60 
61 }

 

(2)nfs_rpcsvc_stage_init函數

首先還是看看這個函數的定義和實現吧:

 1 rpcsvc_stage_t * nfs_rpcsvc_stage_init (rpcsvc_t *svc)
 2 
 3 {
 4 
 5  rpcsvc_stage_t          *stg = NULL;
 6 
 7    int                     ret = -1;
 8 
 9   size_t                  stacksize = RPCSVC_THREAD_STACK_SIZE;
10 
11   pthread_attr_t          stgattr;
12 
13   unsigned int            eventpoolsize = 0;
14 
15 stg = GF_CALLOC (1, sizeof(*stg), gf_common_mt_rpcsvc_stage_t);//分配內存資源
16 
17 eventpoolsize = svc->memfactor * RPCSVC_EVENTPOOL_SIZE_MULT;//計算事件內存池大小
18 
19  stg->eventpool = event_pool_new (eventpoolsize);//分配內存資源
20 
21    pthread_attr_init (&stgattr);//初始化線程熟悉值
22 
23  ret = pthread_attr_setstacksize (&stgattr, stacksize);//設置線程的堆棧大小
24 
25 ret = pthread_create (&stg->tid, &stgattr, nfs_rpcsvc_stage_proc, (void *)stg);//創建線程
26 
27   stg->svc = svc;
28 
29 return stg;
30 
31 }

 

這個函數主要就是啟動一個線程然后開始分發事件,事件分發函數會等待某一個事件的發生,發生以后會執行以前已經注冊的函數指針,在這里就是注冊的是權限操作相關的函數。具體的事件處理和分發過程就不在詳細分析了!

2.nfs_add_all_initiators函數

這個函數主要是添加各個版本的nfs協議的初始化。它三次調用函數nfs_add_initer分別來為mnt3mnt1nfs3版本的nfs協議(各個版本的協議內容見附件)進行初始化。詳細看看nfs_add_initer函數的代碼,如下:

 1 int nfs_add_initer (struct list_head *list, nfs_version_initer_t init)
 2 
 3 {
 4 
 5  struct nfs_initer_list  *new = NULL;
 6 
 7   new = GF_CALLOC (1, sizeof (*new), gf_nfs_mt_nfs_initer_list);//分配內存
 8 
 9  new->init = init;//賦值初始化函數指針
10 
11   list_add_tail (&new->list, list);//添加到協議版本鏈表的末尾
12 
13   return 0;
14 
15 }

 

每個版本的nfs協議都有自己的初始化函數,以便處理那些特殊的協議部分,上面的過程就是將各個版本nfs協議初始化保存到鏈表中,在使用協議的時候以便調用相應的初始化函數初始化相關協議內容。

(1)mnt3版本協議

mnt3版本的nfs協議的實現函數,代碼如下:

 1 rpcsvc_program_t * mnt3svc_init (xlator_t *nfsx)
 2 
 3 {
 4 
 5 struct mount3_state     *mstate = NULL;
 6 
 7   mstate = mnt3_init_state (nfsx);
 8 
 9 mnt3prog.private = mstate;
10 
11   return &mnt3prog;
12 
13 }

 

這個函數代碼很簡單,只有兩個重點內容需要關注,一個結構體和一個函數,結構體就是mount3_state,它的定義如下:

 1 /* Describes a program and its version along with the function pointers
 2 
 3  * required to handle the procedures/actors of each program/version.
 4 
 5  * Never changed ever by any thread so no need for a lock. */
 6 
 7 struct rpc_svc_program {
 8 
 9         struct list_head        proglist;
10 
11         char                    progname[RPCSVC_NAME_MAX];
12 
13         int                     prognum;
14 
15         int                     progver;
16 
17         uint16_t                progport;       /* Registered with portmap */
18 
19         int                     progaddrfamily; /* AF_INET or AF_INET6 */
20 
21         char                    *proghost;      /* Bind host, can be NULL */
22 
23         rpcsvc_actor_t          *actors;        /* All procedure handlers */
24 
25         int                     numactors;      /* Num actors in actor array */
26 
27         int                     proghighvers;   /* Highest ver for program supported by the system. */
28 
29         int                     proglowvers;    /* Lowest ver */
30 
31         /* Program specific state handed to actors */
32 
33         void                    *private;
34 
35         /* An integer that identifies the min auth strength that is required
36 
37          * by this protocol, for eg. MOUNT3 needs AUTH_UNIX at least.
38 
39          * See RFC 1813, Section 5.2.1. */
40 
41         int                     min_auth;
42 
43         /* The translator in whose context the actor must execute. This is
44 
45          * needed to setup THIS for memory accounting to work correctly. */
46 
47         xlator_t                *actorxl;
48 
49 };

 

這個結構體的定義中注釋已經很清晰,就不具體分析了,在看看程序中使用這個結構體的賦值,如下:

 1 rpcsvc_program_t        mnt3prog = {
 2 
 3                         .progname       = "MOUNT3",
 4 
 5                         .prognum        = MOUNT_PROGRAM,
 6 
 7                         .progver        = MOUNT_V3,
 8 
 9                         .progport       = GF_MOUNTV3_PORT,
10 
11                         .progaddrfamily = AF_INET,
12 
13                         .proghost       = NULL,
14 
15                         .actors         = mnt3svc_actors,
16 
17                         .numactors      = MOUNT3_PROC_COUNT,
18 
19 };

 

這個是這個結構體的靜態賦值方式,還有動態的賦值方式,就是上面提到的一個函數mnt3_init_state,也是下面將要分析的,這個函數實現代碼如下:

 1 struct mount3_state * mnt3_init_state (xlator_t *nfsx)
 2 
 3 {
 4 
 5 struct mount3_state     *ms = NULL;
 6 
 7  int                     ret = -1;
 8 
 9   ms = GF_CALLOC (1, sizeof (*ms), gf_nfs_mt_mount3_state);//分配結構體對象內存
10 
11    ms->iobpool = nfsx->ctx->iobuf_pool;//IO緩沖池
12 
13   ms->nfsx = nfsx;//屬於哪一個xlator
14 
15   INIT_LIST_HEAD (&ms->exportlist);//初始化導出列表
16 
17    ret = mnt3_init_options (ms, nfsx->options);//初始化選項信息
18 
19   INIT_LIST_HEAD (&ms->mountlist);//初始化掛載列表
20 
21    LOCK_INIT (&ms->mountlock);//初始化鎖
22 
23    return ms;
24 
25 }

 

上面這個函數最主要的工作還是初始化一些相關的參數和選項,其中主要的的內容還是一個結構體和一個函數,結構體就是mount3_state,它的定義如下:

 1 struct mount3_state {
 2 
 3         xlator_t                *nfsx;
 4 
 5         /* The buffers for all network IO are got from this pool. */
 6 
 7         struct iobuf_pool       *iobpool;
 8 
 9         /* List of exports, can be volumes or directories in those volumes. */
10 
11         struct list_head        exportlist;
12 
13         /* List of current mount points over all the exports from this
14 
15          * server. */
16 
17         struct list_head        mountlist;
18 
19         /* Used to protect the mountlist. */
20 
21         gf_lock_t               mountlock;
22 
23         /* Set to 0 if exporting full volumes is disabled. On by default. */
24 
25         int                     export_volumes;
26 
27         int                     export_dirs;
28 
29 };

 

上面這個結構體基本上描述了mnt3版本的nfs協議的一些狀態信息,注釋中都有具體的描述了,下面這個函數就是針對這些信息做一些初始化的工作,如下:

 1 int mnt3_init_options (struct mount3_state *ms, dict_t *options)
 2 
 3 {
 4 
 5  xlator_list_t   *volentry = NULL;
 6 
 7    int             ret = -1;
 8 
 9 __mnt3_init_volume_export (ms, options);//根據nfs3.export-volumes配置選項設置導出卷的信息
10 
11    __mnt3_init_dir_export (ms, options);//根據nfs3.export-dirs配置選項設置導出目錄的信息
12 
13   volentry = ms->nfsx->children;//初始化xlator的鏈表
14 
15   while (volentry) {//遍歷所有的子xlator
16 
17      gf_log (GF_MNT, GF_LOG_TRACE, "Initing options for: %s", volentry->xlator->name);
18 
19        ret = __mnt3_init_volume (ms, options, volentry->xlator);//初始化xlator的卷信息
20 
21 volentry = volentry->next;//下一個xlator
22 
23   }
24 
25 return ret;
26 
27 }

 

上面的代碼主要是初始化所有子xlator的卷相關信息,調用函數__mnt3_init_volume實現,代碼定義如下(把所有錯誤處理代碼、變量定義和參數檢查刪掉后的代碼):

 1 int __mnt3_init_volume (struct mount3_state *ms, dict_t *opts, xlator_t *xlator)
 2 
 3 {
 4 
 5         uuid_clear (volumeid);//清除uuid,即設置為0
 6 
 7         if (gf_nfs_dvm_off (nfs_state (ms->nfsx)))//關閉動態卷
 8 
 9                 goto no_dvm;
10 
11         ret = snprintf (searchstr, 1024, "nfs3.%s.volume-id", xlator->name);//格式化選項key的字符串
12 
13         if (dict_get (opts, searchstr)) {//根據選項key得到選項的值
14 
15                 ret = dict_get_str (opts, searchstr, &optstr);//得到字符串形式的值
16 
17         } 
18 
19         if (optstr) {//如果不為null
20 
21                 ret = uuid_parse (optstr, volumeid);//根據得到的值解析uuid
22 
23         }
24 
25 no_dvm:
26 
27         ret = snprintf (searchstr, 1024, "nfs3.%s.export-dir", xlator->name);//export-dir選項key
28 
29         if (dict_get (opts, searchstr)) {//同上
30 
31                 ret = dict_get_str (opts, searchstr, &optstr);
32 
33                 ret = __mnt3_init_volume_direxports (ms, xlator, optstr, volumeid);//初始化卷導出目錄
34 
35         }
36 
37         if (ms->export_volumes) {//如果導出卷使能
38 
39                 newexp = mnt3_init_export_ent (ms, xlator, NULL, volumeid);//初始化導出環境
40 
41                 list_add_tail (&newexp->explist, &ms->exportlist);//添加導出列表到導出列表的末尾
42 
43         }
44 
45         ret = 0;
46 
47 err:
48 
49         return ret;
50 
51 }

 

由上面代碼可知:主要在初始化導出目錄和導出環境,具體的實現都是調用相應的函數實現。

總結:這個初始化過程主要是在分配一些資源和建立一些關系,真正處理客戶端請求的功能是在很多注冊的或關聯的函數中,客戶端的某一個請求可能就需要調用一個專門的函數來處理。

(2)mnt1版本協議

這個版本的協議實現基本上和mnt3的實現一樣,很多函數基本都就是調用mnt3的,不同的就是具體描述相關謝謝的結構體內容不同吧,例如有關信息的客戶端請求是執行的處理函數等等。所以不再分析此版本協議初始化。

(3)nfs3版本協議

此版本的nfs協議初始化流程和前面分析的mnt3版本協議基本相同,下面只分析不同的部分,具體流程就不在那么分析了,主要介紹一些重點信息。第一需要介紹的就是nfs3_state結構體,定義如下:

 1 struct nfs3_state {
 2 
 3         /* The NFS xlator pointer. The NFS xlator can be running
 4 
 5          * multiple versions of the NFS protocol.*/
 6 
 7         xlator_t                *nfsx;
 8 
 9         /* The iob pool from which memory allocations are made for receiving
10 
11          * and sending network messages. */
12 
13         struct iobuf_pool       *iobpool;
14 
15         /* List of child subvolumes for the NFSv3 protocol.
16 
17          * Right now, is simply referring to the list of children in nfsx above. */
18 
19         xlator_list_t           *exportslist;
20 
21         struct list_head        exports;
22 
23         /* Mempool for allocations of struct nfs3_local */
24 
25         struct mem_pool         *localpool;
26 
27         /* Server start-up timestamp, currently used for write verifier. */
28 
29         uint64_t                serverstart;
30 
31         /* NFSv3 Protocol configurables */
32 
33         size_t                  readsize;
34 
35         size_t                  writesize;
36 
37         size_t                  readdirsize;
38 
39         /* Size of the iobufs used, depends on the sizes of the three params above. */
40 
41         size_t                  iobsize;
42 
43         unsigned int            memfactor;
44 
45         struct list_head        fdlru;
46 
47         gf_lock_t               fdlrulock;
48 
49         int                     fdcount;
50 
51 };

 

上面的結構體主要是記錄一些nfs3協議運行過程中的狀態信息,每一項的意義代碼中有詳細注釋,理解這些信息對后面其他代碼的理解是有非常大的好處的。在看看下面這個結構體的初始化默認值:

 1 rpcsvc_program_t        nfs3prog = {
 2 
 3                         .progname       = "NFS3",
 4 
 5                         .prognum        = NFS_PROGRAM,
 6 
 7                         .progver        = NFS_V3,
 8 
 9                         .progport       = GF_NFS3_PORT,
10 
11                         .progaddrfamily = AF_INET,
12 
13                         .proghost       = NULL,
14 
15                         .actors         = nfs3svc_actors,
16 
17                         .numactors      = NFS3_PROC_COUNT,
18 
19                         /* Requests like FSINFO are sent before an auth scheme
20 
21                          * is inited by client. See RFC 2623, Section 2.3.2. */
22 
23                         .min_auth       = AUTH_NULL,
24 
25 };

 

在看看里面的nfs3svc_actors這個結構體的值,如下:

 1 rpcsvc_actor_t          nfs3svc_actors[NFS3_PROC_COUNT] = {
 2 
 3         {"NULL",        NFS3_NULL,      nfs3svc_null,   NULL,   NULL},
 4 
 5         {"GETATTR",     NFS3_GETATTR,   nfs3svc_getattr,NULL,   NULL},
 6 
 7         {"SETATTR",     NFS3_SETATTR,   nfs3svc_setattr,NULL,   NULL},
 8 
 9         {"LOOKUP",      NFS3_LOOKUP,    nfs3svc_lookup, NULL,   NULL},
10 
11         {"ACCESS",      NFS3_ACCESS,    nfs3svc_access, NULL,   NULL},
12 
13         {"READLINK",    NFS3_READLINK,  nfs3svc_readlink,NULL,  NULL},
14 
15         {"READ",        NFS3_READ,      nfs3svc_read,   NULL,   NULL},
16 
17         {"WRITE", NFS3_WRITE, nfs3svc_write, nfs3svc_write_vec, nfs3svc_write_vecsizer},
18 
19         {"CREATE",      NFS3_CREATE,    nfs3svc_create, NULL,   NULL},
20 
21         {"MKDIR",       NFS3_MKDIR,     nfs3svc_mkdir,  NULL,   NULL},
22 
23         {"SYMLINK",     NFS3_SYMLINK,   nfs3svc_symlink,NULL,   NULL},
24 
25         {"MKNOD",       NFS3_MKNOD,     nfs3svc_mknod,  NULL,   NULL},
26 
27         {"REMOVE",      NFS3_REMOVE,    nfs3svc_remove, NULL,   NULL},
28 
29         {"RMDIR",       NFS3_RMDIR,     nfs3svc_rmdir,  NULL,   NULL},
30 
31         {"RENAME",      NFS3_RENAME,    nfs3svc_rename, NULL,   NULL},
32 
33         {"LINK",        NFS3_LINK,      nfs3svc_link,   NULL,   NULL},
34 
35         {"READDIR",     NFS3_READDIR,   nfs3svc_readdir,NULL,   NULL},
36 
37         {"READDIRPLUS", NFS3_READDIRP,  nfs3svc_readdirp,NULL,  NULL},
38 
39         {"FSSTAT",      NFS3_FSSTAT,    nfs3svc_fsstat, NULL,   NULL},
40 
41         {"FSINFO",      NFS3_FSINFO,    nfs3svc_fsinfo, NULL,   NULL},
42 
43         {"PATHCONF",    NFS3_PATHCONF,  nfs3svc_pathconf,NULL,  NULL},
44 
45         {"COMMIT",      NFS3_COMMIT,    nfs3svc_commit, NULL,   NULL}
46 
47 };

 

由上面兩個結構體的值可以看出,一個具體版本的nfs協議都有一個對應的結構體描述其基本信息,還有一個結構體存儲了消息與函數的對應關系,當接受到什么消息就執行對應的函數,明白了這一點,其實對於各個版本的協議分析都大同小異了,關鍵就是在各個函數具體的實現了。而開頭就介紹的那個結構體存放的都是一些各個版本不同的信息部分,所以會在rpc_svc_program結構體的private保存(void *類型可以保存任何數據類型,也表示是各個版本的nfs協議的私有部分數據)。

3.nfs_init_subvolumes函數

這個函數完成初始化所有子卷的任務,它首先計算需要分配給inode表使用的緩存大小,然后遍歷存放子卷的鏈表,然后依次調用nfs_init_subvolume函數分別初始化每一個子卷,這個函數定義和實現如下:

 1 int nfs_init_subvolume (struct nfs_state *nfs, xlator_t *xl)
 2 
 3 {
 4 
 5 unsigned int    lrusize = 0;
 6 
 7 int             ret = -1;
 8 
 9 lrusize = nfs->memfactor * GF_NFS_INODE_LRU_MULT;//計算在lru鏈表中inodes的數量
10 
11    xl->itable = inode_table_new (lrusize, xl);//新建一個inode的表
12 
13  ret = 0;
14 
15 err:
16 
17  return ret;
18 
19 }

 

這里最重要的是inode_table_t結構體和inode_table_new函數,inode_table_t定義如下:

 1 struct _inode_table {
 2 
 3         pthread_mutex_t    lock;
 4 
 5         size_t             hashsize;    /* bucket size of inode hash and dentry hash */
 6 
 7         char              *name;        /* name of the inode table, just for gf_log() */
 8 
 9         inode_t           *root;        /* root directory inode, with number 1 */
10 
11         xlator_t          *xl;          /* xlator to be called to do purge */
12 
13         uint32_t           lru_limit;   /* maximum LRU cache size */
14 
15         struct list_head  *inode_hash;  /* buckets for inode hash table */
16 
17         struct list_head  *name_hash;   /* buckets for dentry hash table */
18 
19         struct list_head   active;      /* list of inodes currently active (in an fop) */
20 
21         uint32_t           active_size; /* count of inodes in active list */
22 
23         struct list_head   lru;         /* list of inodes recently used.
24 
25                                            lru.next most recent */
26 
27         uint32_t           lru_size;    /* count of inodes in lru list  */
28 
29         struct list_head   purge;       /* list of inodes to be purged soon */
30 
31         uint32_t           purge_size;  /* count of inodes in purge list */
32 
33         struct mem_pool   *inode_pool;  /* memory pool for inodes */
34 
35         struct mem_pool   *dentry_pool; /* memory pool for dentrys */
36 
37         struct mem_pool   *fd_mem_pool; /* memory pool for fd_t */
38 
39 };

 

結構體中每一項都有詳細的注釋了,就不多解析了,下面繼續分析inode_table_new函數,由於這個函數代碼還是有點點多,所以還是采取分步驟來解析,如下:

第一步:定義一個inode_table_t結構體並且分配內存:

1         inode_table_t *new = NULL;
2 
3         new = (void *)GF_CALLOC(1, sizeof (*new), gf_common_mt_inode_table_t);

 

第二步:初始化各個參數;

第三步:初始化各個鏈表,如下:

1         INIT_LIST_HEAD (&new->active);//初始化激活鏈表
2 
3         INIT_LIST_HEAD (&new->lru);//最近使用鏈表
4 
5         INIT_LIST_HEAD (&new->purge);//清楚了的鏈表

 

第四步:為inode表設置rootinode節點信息:

1 __inode_table_init_root (new);

 

第五步:為inode表初始化鎖。

上面的第四步是操作inode節點相關的信息,在ext2/3文件系統中也有inode節點,所以具體看看inode節點信息的管理和操作,就從初始化一個inode表的根節點開始,定義如下:

 1 static void __inode_table_init_root (inode_table_t *table)
 2 
 3 {
 4 
 5 inode_t     *root = NULL;//定義inode表的根節點
 6 
 7   struct iatt  iatt = {0, };//inode節點的屬性信息
 8 
 9 root = __inode_create (table);//創建一個inode表的根節點
10 
11 iatt.ia_gfid[15] = 1;//inode節點的屬性賦值
12 
13    iatt.ia_ino = 1;
14 
15   iatt.ia_type = IA_IFDIR;
16 
17   table->root = root;//賦值inode表的根節點
18 
19    __inode_link (root, NULL, NULL, &iatt);//inode節點和屬性連接起來
20 
21 }

 

整個inode表的初始化完成根節點創建和屬性的賦值,下面主要看看兩個結構體定義和兩個函數的實現,先看看inodeiatt兩個結構體的定義,他們的定義中包含很多重要的信息,他們的定義如下:

 1 struct _inode {
 2 
 3         inode_table_t       *table;         /* the table this inode belongs to */
 4 
 5         uuid_t               gfid;
 6 
 7         gf_lock_t            lock;
 8 
 9         uint64_t             nlookup;
10 
11         uint32_t             ref;           /* reference count on this inode */
12 
13         ino_t                ino;           /* inode number in the storage (persistent) */
14 
15         ia_type_t            ia_type;       /* what kind of file */
16 
17         struct list_head     fd_list;       /* list of open files on this inode */
18 
19         struct list_head     dentry_list;   /* list of directory entries for this inode */
20 
21         struct list_head     hash;          /* hash table pointers */
22 
23         struct list_head     list;          /* active/lru/purge */
24 
25 struct _inode_ctx   *_ctx;    /* replacement for dict_t *(inode->ctx) */
26 
27 };
28 
29 struct iatt {
30 
31         uint64_t     ia_ino;        /* inode number */
32 
33         uuid_t       ia_gfid;
34 
35         uint64_t     ia_dev;        /* backing device ID */
36 
37         ia_type_t    ia_type;       /* type of file */
38 
39         ia_prot_t    ia_prot;       /* protection */
40 
41         uint32_t     ia_nlink;      /* Link count */
42 
43         uint32_t     ia_uid;        /* user ID of owner */
44 
45         uint32_t     ia_gid;        /* group ID of owner */
46 
47         uint64_t     ia_rdev;       /* device ID (if special file) */
48 
49         uint64_t     ia_size;       /* file size in bytes */
50 
51         uint32_t     ia_blksize;    /* blocksize for filesystem I/O */
52 
53         uint64_t     ia_blocks;     /* number of 512B blocks allocated */
54 
55         uint32_t     ia_atime;      /* last access time */
56 
57         uint32_t     ia_atime_nsec;
58 
59         uint32_t     ia_mtime;      /* last modification time */
60 
61         uint32_t     ia_mtime_nsec;
62 
63         uint32_t     ia_ctime;      /* last status change time */
64 
65         uint32_t     ia_ctime_nsec;
66 
67 };

 

上面的定義代碼中都有很詳細的注釋了,下面繼續看inode節點的創建函數,定義如下:

 1 static inode_t * __inode_create (inode_table_t *table)
 2 
 3 {
 4 
 5 inode_t  *newi = NULL;
 6 
 7    newi = mem_get0 (table->inode_pool);//從inode表中的inode內存池中得到一個inode內存
 8 
 9 newi->table = table;//想創建的inode屬於哪一個inode表
10 
11 LOCK_INIT (&newi->lock);//操作inode節點以前初始化鎖
12 
13 INIT_LIST_HEAD (&newi->fd_list);//初始化各個鏈表
14 
15    INIT_LIST_HEAD (&newi->list);
16 
17    INIT_LIST_HEAD (&newi->hash);
18 
19    INIT_LIST_HEAD (&newi->dentry_list);
20 
21 newi->_ctx = GF_CALLOC (1, (sizeof (struct _inode_ctx) *table->xl->graph->xl_count),
22 
23                                 gf_common_mt_inode_ctx);//為多鍵值對結構體分配內存
24 
25    if (newi->_ctx == NULL) {
26 
27     LOCK_DESTROY (&newi->lock);//釋放鎖
28 
29         mem_put (table->inode_pool, newi);//把inode節點放回內存池
30 
31      newi = NULL;
32 
33        goto out;
34 
35   }
36 
37    list_add (&newi->list, &table->lru);//增加鏈表到最近使用鏈表
38 
39    table->lru_size++;//最近使用鏈表的數量加1
40 
41 out:
42 
43  return newi;
44 
45 }

 

這里面最難懂也最重要的是mem_get0 函數,它的重要就是從inode節點的內存池中獲取一個inode節點對象所需要的內存空間,具體的內存池的管理和分配使用到了slab分配器相關的知識。Slab分配器的思想就是把以前已經分配過的對象內存緩存起來,下一次同類的對象來分配對象就直接從緩存中取得,這樣省去分配和初始化的時間(因為是同樣的內存對象)。除了mem_get0函數其余代碼做一些初始化的相關工作,后面有一個分配多鍵值對的內存結構體需要分配,如果失敗就是歸還內存池和釋放鎖占用的資源。這里可以在學習一點知識就是多鍵值對的結果,定義如下:

 1 struct _inode_ctx {
 2 
 3         union {
 4 
 5                 uint64_t    key; xlator_t   *xl_key;
 6 
 7         };
 8 
 9         union {
10 
11                 uint64_t    value1; void       *ptr1;
12 
13         };
14 
15         union {
16 
17                 uint64_t    value2; void       *ptr2;
18 
19         };
20 
21 };

 

這個結構體的作用是可以有兩種類型的鍵,也可以有兩種類型的值,其中一種可以是任意數據結構,而且這是一種一個鍵對應兩個值的結構,特殊情況特殊的處理,從這里可以學習到,如果以后有一個鍵關聯三個值的時候也可以采取這種方式。雖然這個結構體在這里具體是什么作用還不是很明朗,但是可以肯定的是用處大大的,后面可能會用到。

繼續看__inode_link 函數的定義和實現,代碼如下:

 1 static inode_t * __inode_link (inode_t *inode, inode_t *parent, const char *name, struct iatt *iatt)
 2 
 3 {
 4 
 5 dentry_t      *dentry = NULL;//目錄項和inode相關的變量定義
 6 
 7    dentry_t      *old_dentry = NULL;
 8 
 9   inode_t       *old_inode = NULL;
10 
11   inode_table_t *table = NULL;
12 
13    inode_t       *link_inode = NULL;
14 
15    table = inode->table;
16 
17  if (parent) {
18 
19    if (inode->table != parent->table) {//防止不同的inode表連接起來(成為父子關系)
20 
21        GF_ASSERT (!"link attempted b/w inodes of diff table");
22 
23     }
24 
25 }
26 
27 link_inode = inode;
28 
29   if (!__is_inode_hashed (inode)) {//此inode是否有hash鏈表
30 
31     if (!iatt)//屬性值不能為null
32 
33         return NULL;
34 
35  if (uuid_is_null (iatt->ia_gfid))//uuid不能為null
36 
37            return NULL;
38 
39       uuid_copy (inode->gfid, iatt->ia_gfid);//復制uuid到inode節點
40 
41        inode->ino        = iatt->ia_ino;//賦值inode節點數量
42 
43        inode->ia_type    = iatt->ia_type;//inode節點的類型
44 
45       old_inode = __inode_find (table, inode->gfid);//在inode表里面查找是否存在此inode節點
46 
47 if (old_inode) {
48 
49          link_inode = old_inode;//存在
50 
51        } else {
52 
53           __inode_hash (inode);//不存在進行hash並進入hash鏈表
54 
55     }
56 
57 }
58 
59   if (parent) {//父節點不為null
60 
61     old_dentry = __dentry_grep (table, parent, name);//搜索目錄項
62 
63       if (!old_dentry || old_dentry->inode != link_inode) {//沒有找到目錄項或目錄項不等於當前目錄項
64 
65         dentry = __dentry_create (link_inode, parent, name);//創建一個目錄項
66 
67            if (old_inode && __is_dentry_cyclic (dentry)) {//如果inode已經存在並且目錄項是循環的
68 
69             __dentry_unset (dentry);//取消設置目錄項
70 
71              return NULL;
72 
73           }
74 
75     __dentry_hash (dentry);//hash此目錄項
76 
77             if (old_dentry)
78 
79       __dentry_unset (old_dentry);//取消設置老的目錄項
80 
81   }
82 
83  }
84 
85 return link_inode;
86 
87 }

 

這個函數比較復雜,主要涉及到一個目錄項的操作,目錄項本身有inode節點,也有父節點,還包括很多屬於此目錄項的inode節點,這里使用的鏈表進行管理的,還有可能維護一個hash鏈表。對於目錄項的各種具體操作就不在詳細分析了。畢竟這次的主要任務是分析nfs協議的實現,所以init函數分析到此結束。

4.nfs_init_versions函數

前面主要完成了nfs協議相關信息的靜態內容初始化,這個函數會根據前面的初始化信息執行各個nfs協議版本的初始化函數init,然后會注冊監聽事件來監聽客戶端的請求。這個函數的實現如下:

 1 int nfs_init_versions (struct nfs_state *nfs, xlator_t *this)
 2 
 3 {
 4 
 5 struct nfs_initer_list          *version = NULL;//nfs各個版本協議初始化函數列表
 6 
 7   struct nfs_initer_list          *tmp = NULL;
 8 
 9    rpcsvc_program_t                *prog = NULL;//定義個描述rpc服務程序的結構體
10 
11    int                             ret = -1;
12 
13    struct list_head                *versions = NULL;
14 
15   versions = &nfs->versions;//需要遍歷的協議鏈表
16 
17    list_for_each_entry_safe (version, tmp, versions, list) {//變量所有的nfs協議版本
18 
19     prog = version->init (this);//調用協議版本的初始化函數(前面已經分析了具體的初始化過程)
20 
21       prog->actorxl = this;//執行屬於哪一個xlator
22 
23        version->program = prog;//保存初始化函數返回描述協議的rpc服務程序結構體
24 
25       if (nfs->override_portnum)//是否覆蓋端口
26 
27         prog->progport = nfs->override_portnum;//覆蓋端口
28 
29       ret = nfs_rpcsvc_program_register (nfs->rpcsvc, *prog);//注冊rpc服務監聽端口
30 
31   }
32 
33   return ret;
34 
35 }

 

這個函數的作用主要在初始化由rpc服務相關的內容,某個nfs版本的協議初始化在前面已經分析了,所以這個函數中重點需要分析的內容就是注冊rpc服務的函數了,先看看實現,如下:

 1 int nfs_rpcsvc_program_register (rpcsvc_t *svc, rpcsvc_program_t program)
 2 
 3 {
 4 
 5 rpcsvc_program_t        *newprog = NULL;
 6 
 7   rpcsvc_stage_t          *selectedstage = NULL;
 8 
 9  int                     ret = -1;
10 
11 newprog = GF_CALLOC (1, sizeof(*newprog),gf_common_mt_rpcsvc_program_t);//分配資源
12 
13    memcpy (newprog, &program, sizeof (program));//拷貝
14 
15    INIT_LIST_HEAD (&newprog->proglist);//初始化程序鏈表
16 
17    list_add_tail (&newprog->proglist, &svc->allprograms);//添加到所有程序鏈表的末尾
18 
19  selectedstage = nfs_rpcsvc_select_stage (svc);//選擇rpc服務階段程序
20 
21  ret = nfs_rpcsvc_stage_program_register (selectedstage, newprog);//執行rpc階段程序的注冊
22 
23    ret = nfs_rpcsvc_program_register_portmap (svc, newprog);//注冊本地端口映射服務
24 
25  return ret;
26 
27 }

 

真正實現監聽功能的是在函數nfs_rpcsvc_stage_program_register中,所以下面繼續看這個函數的實現:

 1 int nfs_rpcsvc_stage_program_register (rpcsvc_stage_t *stg, rpcsvc_program_t *newprog)
 2 
 3 {
 4 
 5   rpcsvc_conn_t           *newconn = NULL;
 6 
 7    rpcsvc_t                *svc = NULL;
 8 
 9    svc = nfs_rpcsvc_stage_service (stg);//獲得階段服務程序
10 
11    newconn = nfs_rpcsvc_conn_listen_init (svc, newprog);//創建監聽的socket
12 
13 //注冊監聽事件發生執行的函數
14 
15 if ((nfs_rpcsvc_stage_conn_associate (stg, newconn, nfs_rpcsvc_conn_listening_handler, newconn)) == -1) {
16 
17   }
18 
19  return 0;
20 
21 }

 

這個函數調用nfs_rpcsvc_conn_listen_init函數創建監聽使用的socket並且綁定,開始監聽客戶端的請求,並且初始化一些鏈接相關的狀態信息。具體實現如下:

 1 rpcsvc_conn_t * nfs_rpcsvc_conn_listen_init (rpcsvc_t *svc, rpcsvc_program_t *newprog)
 2 
 3 {
 4 
 5 rpcsvc_conn_t  *conn = NULL;
 6 
 7    int             sock = -1;
 8 
 9 //創建監聽socket對象並且設置相應參數和綁定到對應端口,例如地址重用、設置為非阻塞等
10 
11   sock = nfs_rpcsvc_socket_listen (newprog->progaddrfamily, newprog->proghost, newprog->progport);
12 
13 conn = nfs_rpcsvc_conn_init (svc, sock);//初始化鏈接的核心,例如分配鏈接池等資源
14 
15 nfs_rpcsvc_conn_state_init (conn);//初始化rpc為已連接狀態
16 
17    return conn;
18 
19 }

 

nfs_rpcsvc_stage_program_register中還有一個很重要的函數是nfs_rpcsvc_stage_conn_associate,它關聯一些當有鏈接請求來的時候執行的函數,這里主要是指客戶端鏈接來的時候服務器響應事件時執行的函數。看看是怎么注冊和關聯的,如下:

1 conn->stage = stg;
2 
3    conn->eventidx = event_register (stg->eventpool, conn->sockfd, handler, data, 1, 0);

 

終於到達事件處理的核心函數之一了: event_register事件注冊函數並且返回注冊后id值。這個函數中就一句重點代碼:

1 event_pool->ops->event_register (event_pool, fd, handler, data, poll_in, poll_out);

 

由前面初始化過程可知,這里的event_pool->ops的值如下:

 1 static struct event_ops event_ops_epoll = {
 2 
 3         .new              = event_pool_new_epoll,
 4 
 5         .event_register   = event_register_epoll,
 6 
 7         .event_select_on  = event_select_on_epoll,
 8 
 9         .event_unregister = event_unregister_epoll,
10 
11         .event_dispatch   = event_dispatch_epoll
12 
13 };

 

所以這里就是執行event_register_epoll函數,這個函數會在socket描述符上注冊一些事件,然后廣播一個條件信號,在阻塞的線程就會開始執行並開始調用epoll_wait等待具體的IO事件,當注冊的IO事件響應以后會調用響應的函數處理,上面是注冊了socket讀取事件,也就是如果有客戶端的鏈接請求到來時會執行這里注冊的函數,注冊的函數定義如下:

 1 int nfs_rpcsvc_conn_listening_handler (int fd, int idx, void *data, int poll_in, int poll_out, int poll_err)
 2 
 3 {
 4 
 5 rpcsvc_conn_t           *newconn = NULL;
 6 
 7   rpcsvc_stage_t          *selectedstage = NULL;
 8 
 9   int                     ret = -1;
10 
11   rpcsvc_conn_t           *conn = NULL;
12 
13    rpcsvc_t                *svc = NULL;
14 
15     if (!poll_in)//值處理讀取的IO,這里是指客戶端發出的鏈接請求
16 
17      return 0;
18 
19 conn = (rpcsvc_conn_t *)data;//得到傳輸過來的數據
20 
21   svc = nfs_rpcsvc_conn_rpcsvc (conn);//得到鏈接階段的處理程序
22 
23   newconn = nfs_rpcsvc_conn_accept_init (svc, fd);//接收鏈接請求並且返回一個新的套接字用於通信
24 
25 selectedstage = nfs_rpcsvc_select_stage (svc);//選擇一個rpc階段處理程序(鏈接階段)
26 
27 //已經接受連接,需要關聯下一個階段的事件處理程序:指的應該就是數據傳輸相關,如讀寫等
28 
29  ret = nfs_rpcsvc_stage_conn_associate (selectedstage, newconn, nfs_rpcsvc_conn_data_handler, newconn);
30 
31     return ret;
32 
33 }

 

這個函數的功能就是接受客戶端的鏈接並建立新的套接字用於以后單獨與客戶端通信(傳輸數據),當然這個新的套接字需要注冊相應的讀寫等epoll事件,注冊流程和監聽事件完全一樣,只是不同的參數(socket和事件類型等)而已。這些事件的處理函數也就是在這里傳遞函數指針:nfs_rpcsvc_conn_data_handler函數,當有數據傳輸時就會執行這個函數中的代碼,看看它是怎么處理的:

 1 int nfs_rpcsvc_conn_data_handler (int fd, int idx, void *data, int poll_in, int poll_out, int poll_err)
 2 
 3 {
 4 
 5  rpcsvc_conn_t   *conn = NULL;
 6 
 7    int             ret = 0;
 8 
 9    conn = (rpcsvc_conn_t *)data;
10 
11 if (poll_out)
12 
13     ret = nfs_rpcsvc_conn_data_poll_out (conn);//處理可寫事件(套接字可寫)
14 
15 if (poll_err) {
16 
17        ret = nfs_rpcsvc_conn_data_poll_err (conn);//處理套接字出錯事件
18 
19        return 0;
20 
21    }
22 
23   if ((ret != -1) && poll_in) {//如果處理可寫事件失敗以后就不處理可讀事件了
24 
25      ret = 0;
26 
27       ret = nfs_rpcsvc_conn_data_poll_in (conn);//處理可讀事件
28 
29   }
30 
31   if (ret == -1)
32 
33      nfs_rpcsvc_conn_data_poll_err (conn);//出錯處理
34 
35   return 0;
36 
37 }

 

這個函數基本上就處理客戶端與服務器連接以后的各種可讀可寫事件,具體的處理在各個函數中,就不在詳細分析了,相信到達這里以后就不在有其他的難點了。

到此為止這個nfs協議初始化部分分析完畢!

第三節、fini函數

這個函數和init函數做的基本上是完全相反的工作,主要工作就是卸載掉nfs的各個版本的協議並且釋放各種資源,實現如下:

 1 int fini (xlator_t *this)
 2 
 3 {
 4 
 5 struct nfs_state        *nfs = NULL;
 6 
 7   nfs = (struct nfs_state *)this->private;//從xlator獲得私有數據轉換為struct nfs_state結構體
 8 
 9     nfs_deinit_versions (&nfs->versions, this);//卸載協議
10 
11  return 0;
12 
13 }

 

這個函數代碼簡單,首先從xlator得到struct nfs_state結構體數據,這是在初始化的時候設置的,然后就調用函數nfs_deinit_versions 來完成協議具體卸載。卸載函數定義如下:

 1 int nfs_deinit_versions (struct list_head *versions, xlator_t *this)
 2 
 3 {
 4 
 5  struct nfs_initer_list          *version = NULL;
 6 
 7   struct nfs_initer_list          *tmp = NULL;
 8 
 9   struct nfs_state                *nfs = NULL;
10 
11   nfs = (struct nfs_state *)this->private;
12 
13   list_for_each_entry_safe (version, tmp, versions, list) {//遍歷所有版本的協議
14 
15     if (version->program)
16 
17         nfs_rpcsvc_program_unregister (nfs->rpcsvc, *(version->program));//注銷rpc服務過程
18 
19 list_del (&version->list);//從版本鏈表中依次刪除
20 
21 GF_FREE (version);//釋放內存資源
22 
23   }
24 
25  return 0;
26 
27 }

 

整個過程都比較簡單就不在詳細分析卸載過程了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM