linux内核版本:3.10.0
挂载nfs版本:-o vers=4.0
挂载命令mount -t nfs -o vers=4.0 127.0.0.1:/ /mnt/nfstest
nfs-ganesha、server版本:2.7.3
关键的几个函数:
nfs_fs_mount: mount入口
nfs_validate_mount_data: 解析参数入口(nfs_parse_mount_options、nfs_parse_devname)
nfs4_try_mount:开始挂载
nfs4_remote_mount: 获取NFS4下的superblock,superblock向系统提供一个入口
几个关键结构体
1、NFS协议的superblock: nfs_server
/*
* NFS client parameters stored in the superblock.
*/
struct nfs_server {
struct nfs_client * nfs_client; /* shared client and NFS4 state */
/**/
}
在nfs4_remote_mount中申请并初始化一个nfs_server。
nfs_server是文件系统超级块结构(struct super_block)中与NFS文件系统本身相关的字段,也就是struct super_block结构中的字段s_fs_info。和其他FS一样,mount时候创建super_block,系统中super_block形成一个链表。
例: 在nfs_initialise_sb函数中
struct nfs_server *server = NFS_SB(sb);
static inline struct nfs_server *NFS_SB(const struct super_block *s)
{
return (struct nfs_server *)(s->s_fs_info);
}
2、NFS客户端的状态: nfs_client
/*
* The nfs_client identifies our client state to the server.
*/
struct nfs_client {
/**/
}
如果系统挂在了下面两个NFS,系统会创建两个nfs的super_block,对应两个nfs_server。但由于server地址相同,且NFS协议相同,所以系统只创建一个nfs_client。也就是两个nfs_server中的nfs_client指向同一块nfs_client地址,详见nfs4_set_client函数中nfs_get_client的处理。
3、NFS协议的inode: nfs_inode
nfs在客户端持有的inode结构体:
/*
* nfs fs inode data in memory
*/
struct nfs_inode {
/**/
}
NFS挂载关键函数分析
从入口函数nfs_fs_mount分析
1、nfs挂载参数解析
在函数nfs_validate_mount_data中对mount命令进行解析:
nfs_parse_mount_options:
localhost kernel: NFS: nfs mount opts='vers=4.0,addr=127.0.0.1,clientaddr=127.0.0.1'
localhost kernel: NFS: parsing nfs mount option 'vers=4.0'
localhost kernel: NFS: parsing nfs mount option 'addr=127.0.0.1'
localhost kernel: NFS: parsing nfs mount option 'clientaddr=127.0.0.1'
nfs_parse_devname:
localhost kernel: NFS: MNTPATH: '/'
2、实际挂载——获取superblock
nfs4_remote_mount函数做申请并初始化一个superblock的任务:
--> nfs4_create_server: 初始化nfs_server,并且将nfs_server和nfs_client关联在一起
##--> nfs4_init_server
####--> nfs4_set_client
######--> nfs_get_client: Allocate or find a client reference we can use
########--> nfs_match_client:匹配下是否已有链接到该server的nfs
##########--> nfs4_init_client:未匹配到,进入init_client,初始化nfs_client
##########--> nfs_create_rpc_client:创建rpc client
##########--> nfs4_init_client_minor_version: 小版本nfs协议初始化
##########--> nfs4_discover_server_trunking: 发现server trunking
############-->nfs40_discover_server_trunking: 小版本处理
##############-->nfs4_proc_setclientid:与server协商clientid
############-->nfs40_walk_client_list
##############-->nfs4_proc_setclientid_confirm:对clientid进行一次确认处理
####--> nfs_init_server_rpcclient
##--> nfs4_server_common_setup: 对nfs server的一些设置
####--> nfs4_init_session
####--> nfs4_get_rootfh: Probe the root fh to retrieve its FSID and filehandle
######--> nfs4_proc_get_rootfh
##--> nfs_probe_fsinfo
--> nfs_fs_mount_common
在nfs4_init_client中对nfs_client初始化设置回调nfs_v4_clientops。
如果nfs_match_client中匹配到已有该server的挂载,则返回已有的该server使用的nfs_client,如果没有该server的其他nfs挂载,则新建一个用于该server的nfs_client,进入nfs4_init_client逻辑,初始化回调信息等。
nfs_create_rpc_client中通过sunRPC下的rpc_create,尝试与server建立连接,协商是否支持RPC_AUTH_GSS_KRB5I版本的RPC,不支持则尝试RPC_AUTH_UNIX。
nfs4_init_client是对NFSv4.x版本做一个初始化,还要再进入nfs4_init_client_minor_version做一下小版本的初始化动作,NFSv4.0使用函数nfs40_init_client,NFSv4.1和NFSv4.2共用函数nfs41_init_client。
在nfs4_lookup_root中,通过发送LOOKUP_ROOT的RPC指令,获取root fh,并得到FSID
发出去的消息结构体:
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP_ROOT],
.rpc_argp = &args,
.rpc_resp = &res,
};
获取到FSID与root FH:
localhost kernel: Server FSID: 98:98
localhost kernel: Pseudo-fs root FH at ffff88085cc07140 is 128 bytes, crc: 0x7133d1b0:
localhost kernel: 43000000 7bcc3946 2664c9fa c301002f
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
localhost kernel: 00000000 00000000 00000000 00000000
关于clientid
NFSv2和NFSv3是无状态协议,服务器不需要记录客户端状态,只需要监听并响应客户端的请求就可以了。而NFSv4是一种有状态的协议,服务器需要记录客户端的状态,需要处理客户端的各种异常情况。NFSv4中,每个客户端用一个8字节的数字表示,这个数据称为clientid,服务器端可以保证任何两个客户端的clientid不相同。与clientid相关的两个请求是SETCLIENTID和SETCLIENTID_CONFIRM。SETCLIENTID的作用是为客户端创建一个clientid,同时返回一个verifier,verifier是一个验证信息。SETCLIENTID_CONFIRM的作用是向服务器确认clientid。客户端中,clientid和verifier保存在nfs_client结构中。
client端发起clientid协商请求
nfs4_proc_setclientid:向server发起clientid协商,发出SETCLIENTID的RPC请求
ganesha server端接收到请求
-->nfs4_Compound:处理RPC请求
##-->nfs4_op_setclientid:创建一个新的clientid
####-->create_client_id:创建clientid
server成功计算出clientid结果,clientid结果为0x5cee3b20
nfs4_op_setclientid :CLIENT ID :DEBUG :SETCLIENTID reply Verifier=0100000000000000 0x7f3ae0002630 ClientID={Epoch=0x5cee3b20 Counter=0x00000001} UNCONFIRMED Client={0x7f3ae0002530 name=(45:Linux NFSv4.0 10.192.52.204/10.192.52.168 tcp) refcount=2} t_delta=0 reservations=0 refcount=1 cb_prog=1073741824 r_addr=10.192.52.204.235.204 r_netid=tcp
SETCLIENTID请求中,服务器为客户端设置了clientid,但是客户端不能马上使用这个clientid,必须先向服务器发起SETCLIENTID_CONFIRM请求,确认接收到了clientid,然后才能使用。
nfs4_proc_setclientid_confirm:client向server发起确认clientid
server端回复确认clientid
nfs4_op_setclientid_confirm:
参考资料
NFS客户端的数据结构:https://blog.csdn.net/ycnian/article/details/8511060
NFS Client in Linux Kernel: https://www.jianshu.com/p/79d211508026
clientid:https://blog.csdn.net/ycnian/article/details/8559108
SETCLIENTID_CONFIRM:https://blog.csdn.net/ycnian/article/details/8561524
SETCLIENTID: https://blog.csdn.net/ycnian/article/details/8561484
NFS 文件系统源代码剖析: https://www.ibm.com/developerworks/cn/linux/l-cn-nfs/index.html
NFS4文件锁机制探秘: https://www.cnblogs.com/zhenjing/archive/2011/05/15/NFS4_lock.html