NFSv2和NFSv3是无状态协议,服务器不需要记录客户端状态,只需要监听并响应客户端的请求就可以了。而NFSv4是一种有状态的协议,服务器需要记录客户端的状态,需要处理客户端的各种异常情况。NFSv4中,每个客户端用一个8字节的数字表示,这个数据称为clientid,服务器端可以保证任何两个客户端的clientid不相同。与clientid相关的两个请求是SETCLIENTID和SETCLIENTID_CONFIRM。SETCLIENTID的作用是为客户端创建一个clientid,同时返回一个verifier,verifier是一个验证信息。SETCLIENTID_CONFIRM的作用是向服务器确认clientid。客户端中,clientid和verifier保存在nfs_client结构中。
#define NFS4_VERIFIER_SIZE 8typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;struct nfs_client { .... u64 cl_clientid; // clientid nfs4_verifier cl_confirm; // 一个确认信息 unsigned long cl_lease_time; // cientid的有效期 unsigned long cl_last_renewal;// 协商clientid的时间 ....}
clientid具有一个有效期,当超过期限后clientid就无效了。
1.SETCLIENTID参数
客户端通过SETCLIENTID请求向服务器申请一个clientid,SETCLIENTID请求参数的数据结构如下:
struct SETCLIENTID4args {
nfs_client_id4 client; // 客户端信息
cb_client4 callback; // CALLBACK信息
uint32_t callback_ident; // 客户端的编号
};
SETCLIENTID请求中包含三个字段:
client:这是客户端的标识信息,可以唯一表示一个客户端。如果一个系统中包含多个客户端,则这些客户端发起SETCLIENTID请求时,报文中的client字段一定不相同,因此服务器可以区分不同的客户端。client字段中还必须包含客户端重启的信息,当客户端重启后client字段必须不同,因此服务器可以检测到客户端重启了。
callback:因为NFSv4中引入了delegation,因此客户端和服务器之间需要建立一个反向通道,当文件冲突时服务器向客户端发送RECALL请求。callback中包含了反向通道的信息。如CALLBACK服务编号,CALLBACK服务中RPC服务器的IP地址、端口号以及传输层协议。
callback_ident:客户端可以挂载多个NFS文件系统,当客户端接收到RECALL请求后如何确定是哪个文件系统呢?callback_ident是客户端为NFS文件系统设置的一个编号,SETCLIENTID请求中客户端将这个编号传递给服务器,服务器发起RECALL请求时报文中包含了这个编号,客户端将报文中的编号和所有文件系统中的编号进行比较,这样就可以确定是哪个文件系统了。
struct nfs_client_id4 {
verifier4 verifier; // 8字节
opaque id<NFS4_OPAQUE_LIMIT>; // 最多1024字节
};
nfs_clientid4是客户端信息的数据结构,这个结构包含两个字段:
verifier:这是标识客户端重启信息的字段,固定长度为8字节。当客户端重启后这个字段必须不同。RFC3530没有规定这个字段的构成方式,Linux系统中这个字段是客户端的开机时间(确切说是nfs模块的加载时间),当客户端宕机重启后这个值一定会发生变化。
id:这个字段表示了客户端的信息,同一个系统中任意两个客户端的信息不可能相同,同时必须保证客户端重启后这个字段的值不变。这是一个变长字段,长度不能超过1024字节。RFC3530没有规定这个字段的构成方式,但是给出了建议方式。Linux系统中这个字段由客户端地址、服务器端地址和传输层协议构成。
struct cb_client4 {
unsigned int cb_program; // CALLBACK服务编号
clientaddr4 cb_location; // CALLBACK服务地址
};
cb_client4中包含了CALLBACK服务的信息,这个结构包含两个字段:
cb_program:这是CALLBACK服务的编号,这个编号固定为0x40000000。
cb_location:这是CALLBACK服务中RPC服务器的地址,其实就是NFS客户端的地址。CALLBACK服务器中,NFS服务器是RPC客户端,NFS客户端是RPC服务器,NFS服务器向客户端发送请求。cb_location中包含了RPC服务器的IP地址、端口号和传输层协议。
2.SETCLIENTID返回值
NFS服务器接收到SETCLIENTID请求后会根据请求报文中的信息查找是否已经保存了这个客户端的信息。服务器将client.id中的信息跟服务器已经建立的客户端进行比较,如果找不到匹配条目就说明这是一个新的客户端,就创建一个新的客户端条目,并设置clientid。如果查找到了匹配的条目,然后比较client.verifier。如果不同说明客户端重启了,服务器删除客户端条目中保存的各种状态信息,然后创建一个新的客户端条目。如果client.verifier相同,服务器认为客户端没有重启,只是想更新CALLBACK服务的信息。正常情况下,处理完毕后服务器会将clientid返还给客户端,SETCLIENTID应答报文的结构如下:
struct SETCLIENTID4resok {
clientid4 clientid; // 8字节
verifier4 setclientid_confirm; // 8字节
};
clientid:这是服务器为客户端设置的一个标识,固定长度为8字节。每个clientid代表一个客户端,服务器可以保证系统中任意两个客户端的clientid都不相同。并且clientid中还保存了服务器的重启信息。Linux中clientid中包含了服务器的开机时间(确切来说是nfsd模块的加载时间)表示服务器的重启信息。
setclientid_confirm:这是一个客户端确认信息。
3.SETCLIENTID_CONFIRM参数
客户端通过SETCLIENTID请求获取clientid后不能马上使用,必须向服务器发起SETCLIENTID_CONFIRM请求,告诉服务器自己收到了clientid,SETCLIENTID_CONFIRM请求结束后才能使用client。SETCLIENTID_CONFIRM请求报文中的数据就是SETCLIENTID应答报文中的数据。
struct SETCLIENTID4resok {
clientid4 clientid; // 8字节
verifier4 setclientid_confirm; // 8字节
};
4.SETCLIENTID_CONFIRM返回值
服务器接收到SETCLIENTID_CONFIRM请求后会检查clientid是否过期了(因为clientid中保存了服务器的重启信息),Linux系统中是通过比较服务器的开机时间和clientid中保存的开机时间实现的。如果clientid过期了会返回错误码NFS4ERR_STALECLIENTID。客户端接收到这个错误码后会重新发起SETCLIENTID请求协商新的clientid。如果clientid没有过期,服务器会查找是否存在这个客户端,找到后向客户端返回NFS4ERR_OK,表示确认无误,客户端就可以使用这个clientid了。
最后还需要说说setclientid_confirm字段的含义。服务器根据clientid查找客户端,那么setclientid_confirm做什么呢?SETCLIENTID中存在一种特殊情况,客户端可以通过SETCLIENTID请求更新CALLBACK的信息,这种情况下clientid不会发生变化(因为clientid只能区分不同的客户端以及客户端是否重启了),那么客户端和服务器如何确认CALLBACK信息已经更新了呢?因此SETCLIENTID返回了一个字段setclientid_confirm,只要客户端信息发生了变化setclientid_confirm就会发生变化。因此,服务器除了需要比较clientid,还需要比较setclientid_confirm,只有这两个条件同时满足时才能返回NFS4ERR_OK。
5.clientid协商过程
下面是Linux中协商clientid的过程。
int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{
// 这是SETCLIENTID请求的返回结果
// 先取出这两个值,是为了直接do_confirm.
struct nfs4_setclientid_res clid = {
// clientid代表一个NFS客户端,以后就使用这个clientid代表这个客户端了.
.clientid = clp->cl_clientid,
// 这是一个确认值
.confirm = clp->cl_confirm,
};
unsigned short port;
int status;
// 已经发送了SETCLIENTID请求,只需要发送SETCLIENTID_CONFIRM请求就可以了
// SETCLIENTID请求会设置这个标志位.
if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
goto do_confirm; // 跳过SETCLIENTID请求,直接发起SETCLIENTID_CONFIRM请求.
// 这是CALLBACK服务使用的端口号
port = nfs_callback_tcpport;
if (clp->cl_addr.ss_family == AF_INET6)
port = nfs_callback_tcpport6;
// 向NFS服务器发送SETCLIENTID请求,结果保存在clid中
status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
if (status != 0) // RPC执行过程出错.
goto out;
// 已经协商了clientid,但是还没有确认,还不能使用.
clp->cl_clientid = clid.clientid;
clp->cl_confirm = clid.confirm;
// 当需要执行SETCLIENTID_CONFIRM请求时设置这个标志位
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
do_confirm:
// 向NFS服务器发送SETCLIENTID_CONFIRM请求
// 当执行成功后,这个函数更新了clp->cl_lease_time和clp->cl_last_renewal.
// 如果执行失败的话,就不更新这两个值了.
status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
if (status != 0)
goto out;
// SETCLIENTID_CONFIRM请求执行完毕,清除这个标志位 clientid经过了确认,可以正常使用了.
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
// nfs4_schedule_state_renewal()是NFSv4中管理客户端状态的函数,
// 以后再介绍这个函数吧.
nfs4_schedule_state_renewal(clp);
out:
return status;
}
今天的文章clientid_电脑怎么查看设备ID[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/69884.html