您的位置:首页 > 博客中心 > 网络系统 >

Linux内核RPC请求过程

时间:2022-04-03 09:02

  • /* 
  •  */  
  • svc_process(struct svc_rqst *rqstp)  
  •         // 这是一块缓存,server从网卡中接收到RPC消息后存放在这里(已经去掉了IP头,TCP或UDP报文头)  
  •         struct kvec             *argv = &rqstp->rq_arg.head[0];  
  •         struct kvec             *resv = &rqstp->rq_res.head[0];  
  •         u32                     dir;  
  •         /* 
  •          * Initially it has just one page 
  •         rqstp->rq_resused = 1;  
  •         resv->iov_base = page_address(rqstp->rq_respages[0]);  
  •         rqstp->rq_res.pages = rqstp->rq_respages + 1;  
  •         rqstp->rq_res.page_base = 0;  
  •         rqstp->rq_res.buflen = PAGE_SIZE;  
  •         rqstp->rq_res.tail[0].iov_len = 0;  
  •         rqstp->rq_xid = svc_getu32(argv);   // 这里開始解析RPC报文了,依照RPC报文格式,这是RPC消息的XID。  
  •         dir  = svc_getnl(argv);   // 这个函数解析出了RPC保本中的第二个字段(Message Type)  
  •                 /* direction != CALL */  
  •                 serv->sv_stats->rpcbadfmt++;  
  •                 return 0;  
  •   
  •         // 返回值1表示处理过程正常,已经正常填充了RPC应答消息,能够发送给client了。  
  •         /* Returns 1 for send, 0 for drop */  
  •                 return svc_send(rqstp);    // 这是发送RPC应答消息的报文,不会深入分析这个函数了。  
  •                 svc_drop(rqstp);    // 丢弃RPC报文。  
  •         }  
  • svc_putu32(resv, rqstp->rq_xid);        // 组装RPC应答报文中的第一个字段XID  
  •   
  • svc_putnl(resv, 1);             // 组装RPC应答报文中第二个字段Message Type,RPC应答消息中这个字段固定为1.  
  • // 眼下通用的RPC版本号是2,server端仅仅支持RPC版本号2。假设不是版本号2,则不处理了。  
  •         goto err_bad_rpc;  
  • // 组装RPC应答消息的第三个字段 Reply State,这里临时将这个字段设置为0了,表示正常。  
  • // reply_statp就是指向了这个字段的位置,当出错后能够依据reply_statp指针找到这个字段。  
  • svc_putnl(resv, 0);             /* ACCEPT */  

  • // 解析RPC请求消息中第4个字段Program,这个字段是RPC服务程序(如NFS服务)的编号  
  • // 解析RPC请求消息中第5个字段Version,这个字段是RPC服务程序的版本   
  • // 解析RPC请求消息中第6个字段Procedure,这个字段是RPC服务例程的编号  
  • auth_res = svc_authenticate(rqstp, &auth_stat);  
  • if (auth_res == SVC_OK && progp) {  
  •         auth_res = progp->pg_authenticate(rqstp);  
  • int  
  • {  
  •         struct auth_ops         *aops;  
  •         *authp = rpc_auth_ok;  
  •         // 前面的函数已经解析出了RPC请求报文中前6个字段,以下该解析第7个字段Credential了。  
  •         flavor = svc_getnl(&rqstp->rq_arg.head[0]);  
  •         dprintk("svc: svc_authenticate (%d)\n", flavor);  
  •         spin_lock(&authtab_lock);  
  •         // authtab[flavor]是这样的认证方式的操作函数集合  
  •             !try_module_get(aops->owner)) {  
  •                 *authp = rpc_autherr_badcred;   // 不支持这样的认证方式,这是错误码  
  •         }  
  •   
  •         // 调用详细认证方式中的accept()函数解析RPC报文中的认证信息。  
  •         return aops->accept(rqstp, authp);   
  • enum rpc_auth_flavors {  
  •     RPC_AUTH_UNIX  = 1,  
  •     RPC_AUTH_DES   = 3,  
  •     RPC_AUTH_GSS   = 6,  
  •     /* pseudoflavors: */  
  •     RPC_AUTH_GSS_KRB5I = 390004,  
  •     RPC_AUTH_GSS_LKEY  = 390006,  
  •     RPC_AUTH_GSS_LKEYP = 390008,  
  •     RPC_AUTH_GSS_SPKMI = 390010,  
  • };  
  • struct auth_ops {  
  •         struct module *owner;  
  •         int     (*accept)(struct svc_rqst *rq, __be32 *authp);  
  •         void    (*domain_release)(struct auth_domain *);  
  • };  
  • progp = serv->sv_program;       // 取出这个端口中注冊的RPC服务处理程序  
  • // 每一个端口上能够注冊多种RPC服务,这些RPC服务的处理程序构成了一个链表,  
  • for (progp = serv->sv_program; progp; progp = progp->pg_next)  
  •                 break;    
  • // progp就是找到的处理程序。假设遍历到链表结尾也沒有找到编号相等的处理程序,  
  • if (progp == NULL)      // 假设沒有处理例程,退出。  
  •   
  • if (vers >= progp->pg_nvers ||  
  •         goto err_bad_vers;  
  • // versp是RPC处理程序中的一个版本号,每一个版本号的处理程序中包括多个处理例程,  
  • // procp->pc_func就是这个处理例程的处理函数了,假设server端沒有实现这个例程,  
  • procp = versp->vs_proc + proc;      // 取出处理例程  
  •         goto err_bad_proc;  
  •   
  • serv->sv_stats->rpccnt++;  
  • /* Build the reply header. */  
  • // 这是RPC应答消息的第5个字段Accept State,先初始化为RPC_SUCCESS。  
  • svc_putnl(resv, RPC_SUCCESS);       // 认证通过  
  • struct svc_procedure {  
  •         svc_procfunc            pc_func;        /* process the request */  
  •         // 这个函数负责解析这些内容  
  •         // 这是RPC请求的编码函数,server端须要将pc_func的处理结果封装到  
  •         kxdrproc_t              pc_encode;      /* XDR encode result */  
  •         kxdrproc_t              pc_release;     /* XDR free result */  
  •         unsigned int            pc_argsize;     /* argument struct size */  
  •         unsigned int            pc_ressize;     /* result struct size */  
  •         unsigned int            pc_count;       /* call count */  
  •         // 就不处理了,直接将缓存中的数据返回给client就能够了。  
  •         // 这是调整RPC应答消息缓存的一个数据量  
  • };  

  • struct svc_version {  
  •         u32                     vs_vers;        /* version number */  
  •         u32                     vs_nproc;       /* number of procedures */  
  •         // 这个版本号中全部例程的处理函数都在这里,各个例程按顺序排列。  
  •         // 这也是从组装应答消息相关的缓存的一个长度  
  •   
  •         unsigned int            vs_hidden : 1;  /* Don‘t register with 端口mapper. 
  •   
  •          * A return value of 0 means drop the request. 
  •          */  
  •         // NFS中这个函数是nfsd_dispatch().  
  • };  

  • struct svc_program {  
  •         struct svc_program *    pg_next;        /* other programs (same xprt) */  
  •         u32                     pg_prog;        /* program number */  
  •         unsigned int            pg_lovers;      /* lowest version */  
  •         unsigned int            pg_hivers;      /* lowest version */  
  •         unsigned int            pg_nvers;       /* number of versions */  
  •         struct svc_version **   pg_vers;        /* version array */  
  •         char *                  pg_name;        /* service name */  
  •         char *                  pg_class;       /* class name: services sharing authentication */  
  •         struct svc_stat *       pg_stats;       /* rpc statistics */  
  •         int                     (*pg_authenticate)(struct svc_rqst *);  
  • /* Call the function that processes the request. */  
  •         /* Decode arguments */  
  •         // 開始解码了,解析RPC报文中的数据  
  •                 goto err_garbage;  
  •         // 处理请求  
  •   
  •         if (rqstp->rq_dropme) {  
  •                         procp->pc_release(rqstp, NULL, rqstp->rq_resp);  
  •         }         
  •         if (*statp == rpc_success &&  
  •             !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {  
  •                 /* serv->sv_stats->rpcsystemerr++; */  
  •         }         
  •         dprintk("svc: calling dispatcher\n");  
  •                 /* Release reply info */   
  •                         procp->pc_release(rqstp, NULL, rqstp->rq_resp);  
  •         }         
  • }  

  • 经过步骤1--步骤4的处理,我们已经解析了RPC请求报头的数据,找到了RPC请求的处理函数,最后一步就是開始处理这个请求了。处理一个RPC请求的函数是svc_version结构中的vs_dispatch函数。假设RPC程序未定义这个函数,就依照标准的流程进行处理。在标准的流程中,首先调用svc_procedure结构中的pc_decode函数,这个函数的内容是解析RPC报文的净荷,对于NFS服务来说,这个函数的作用就是解析RPC报文中的NFS数据,这些数据就是处理函数的參数。真正的处理函数是svc_procedure结构中的pc_func函数,每一个例程都须要定义自己的处理函数。处理完毕后,须要将处理结果封装在RPC应答报文中返回给client。比方对于READ操作,我们须要将读取的数据封装在RPC报文中返回,这个封装过程是由svc_procedure结构中的pc_encode函数实现的。

    NFS服务定义了自己的vs_dispatch函数,NFSV2、NFSV3、NFSV4使用了同一个vs_dispatch函数,这个函数的定义是nfsd_dispatch,这个函数定义在fs/nfsd/nfssvc.c中,处理流程基本上和上面讲的流程同样,就不解说了。

    Linux内核RPC请求过程,布布扣,bubuko.com

    本类排行

    今日推荐

    热门手游