1.CLOSE和OPEN_DOWNGRADE
客户端用户执行完读写操作之后就需要关闭文件了,一般情况下客户端用户调用close(2)关闭文件。NFS文件系统中有两个请求与关闭文件相关:CLOSE和OPEN_DOWNGRADE。
CLOSE:这个请求的作用是释放OPEN请求中申请的stateid。客户端在OPEN操作中向服务器申请了stateid,stateid表示了客户端用户请求的对文件的访问权限。当执行CLOSE请求后,客户端用户就放弃了这种访问权限,就不能继续使用这个stateid读写文件了。如果想再次访问文件,必须再次执行OPEN操作申请访问权限。
OPEN_DOWNGRADE:表示用户释放了部分访问权限,比如用户申请了读权限和写权限,可以通过OPEN_DOWNGRADE放弃部分访问权限。
下面先以几个具体例子说明什么情况下会发起CLOSE请求,什么情况下会发起OPEN_DOWNGRADE请求。
情况1:
fd1 = open(file1, O_RDONLY);
close(fd1);
这是最简单的情况,当执行close(fd1)时会向服务器发起CLOSE请求。
情况2:
fd1 = open(file1, O_RDONLY);
fd2 = open(file1, O_RDWR);
close(fd2);
close(fd1);
执行close(fd2)时会向服务器发起OPEN_DOWNGRADE请求,放弃写权限。执行close(fd1)时会向服务器发起CLOSE请求。
情况3:
fd1 = open(file1, O_RDONLY);
fd2 = open(file1, O_RDWR);
close(fd1);
close(fd2);
执行close(fd1)时不会向服务器发起任何请求,因为fd2仍然需要读写权限,执行close(fd2)时会向服务器发起CLOSE请求。
情况4:
fd1 = open(file1, O_RDONLY);
fd2 = open(file1, O_RDONLY);
close(fd1);
close(fd2);
执行close(fd1)时不会向服务器发起任何请求,因为fd2仍然需要读权限,执行close(fd2)时会向服务器发起CLOSE请求。
总结:如果执行open(2)后用户的访问权限不变,则不会向服务器发起任何请求。如果执行open(2)后用户的访问权限变小了,但是仍有访问权限,则执行open(2)时向服务器发起OPEN_DOWNGRADE请求。如果执行open(2)后用户没有任何访问权限了,则执行open(2)时向服务器发起CLOSE请求。
情况5:
如果用户通过delegation访问文件,则关闭文件时不会执行任何请求。
user1:
fd1 = open(file1, O_RDONLY);
sleep(100);
close(fd1);
user2:
fd2 = open(file1, O_RDONLY);
close(fd2);
在这个例子中,user1先以只读权限打开了文件file1(假设服务器分配了delegation),然后等待了100秒。在user1等待的过程中user2也执行了open()操作,然后马上执行了close()。执行close(fd2)不会发起任何请求,执行close(fd1)时会向服务器发起CLOSE请求。
2.客户端代码
当用户执行close(2)时,客户端会执行到函数__nfs4_close(),这个函数代码如下:
参数state:这是OPEN操作中创建的nfs4_state结构,包含了申请的stateid.
参数fmode:这是关闭的访问权限。
参数gfp_mask:分配内存的标志位
参数wait:同步操作还是异步操作
static void __nfs4_close(struct nfs4_state *state,
fmode_t fmode, gfp_t gfp_mask, int wait)
{
// 找到nfs4_state_owner结构,这里包含了用户信息.
struct nfs4_state_owner *owner = state->owner;
int call_close = 0;
fmode_t newstate;
atomic_inc(&owner->so_count); // 先增加引用计数,处理完毕后再减少引用计数.
/* Protect against nfs4_find_state() */
spin_lock(&owner->so_lock); // 加锁
// 先减少相应访问权限的计数.
switch (fmode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ: // 我们释放的是读权限
state->n_rdonly--;
break;
case FMODE_WRITE: // 我们释放的是写权限
state->n_wronly--;
break;
case FMODE_READ|FMODE_WRITE: // 我们释放的是读写权限
state->n_rdwr--;
}
// newstate表示剩余的访问权限
newstate = FMODE_READ|FMODE_WRITE;
// 读写权限的计数为0了,说明没有以O_RDWR权限打开文件的进程了
if (state->n_rdwr == 0) {
if (state->n_rdonly == 0) { // 以O_RDONLY权限打开文件的进程数量也为0了
newstate &= ~FMODE_READ; // 表示可以释放读权限了
call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
}
if (state->n_wronly == 0) { // 以O_WRONLY权限打开文件的进程数量为0了
newstate &= ~FMODE_WRITE; // 表示可以释放写权限了
call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
}
if (newstate == 0)
clear_bit(NFS_DELEGATED_STATE, &state->flags);
}
// 设置nfs4_state结构中现在的访问权限.
nfs4_state_set_mode_locked(state, newstate);
spin_unlock(&owner->so_lock); // 解锁
// 访问权限没有变化,不需要向服务器发起CLOSE或者OPEN_DOWNGRADE请求
if (!call_close) {
nfs4_put_open_state(state); // 只需减少state引用计数就可以了,当计数减到0时释放内存.
nfs4_put_state_owner(owner); // 减少owner引用计数
} else {