1. System Call
- flock
- fcntl
2. 內(nèi)核中的Lock
分為兩種lock璧疗,但在內(nèi)核中統(tǒng)一成file_lock
數(shù)據(jù)結(jié)構(gòu)
- FL_POSIX,來自fnctl系統(tǒng)調(diào)用,具體參見
flock64_to_posix_lock()
- FL_FLOCK,來自flock系統(tǒng)調(diào)用,具體參見
flock_make_lock()
2.1 NFS的特殊處理
以下兩種函數(shù),最終都統(tǒng)一調(diào)用nfs4_proc_lock
- nfs_lock //for posix
- nfs_flock //for flock
對于posix lock入口函數(shù)是nfs4_proc_lock()
瞬哼,分別調(diào)用:
- nfs4_proc_getlk():用于查詢 ,對應(yīng)LOCKT NFS命令
- nfs4_proc_unlck():用于解鎖,對應(yīng)LOCKU NFS命令
- nfs4_proc_setlk():用于加鎖,對應(yīng)LOCK NFS命令
3. NFS協(xié)議中的Lock Owner
對于普通的文件系統(tǒng)來說,flock的owner是內(nèi)核下的open fd,posix lock的owner是current->files。如果Owner一樣右核,說明lock請求來自同一個實(shí)體,判斷l(xiāng)ock是否沖突時候直接跳過渺绒。對于NFS來說贺喝,協(xié)議規(guī)定由這個數(shù)據(jù)結(jié)構(gòu)表示owner(其實(shí)open的owner也同樣由這個數(shù)據(jù)結(jié)構(gòu)表示):
摘自nfs4.1協(xié)議
struct state_owner4 {
clientid4 clientid;
opaque owner<NFS4_OPAQUE_LIMIT>;
};
- clientid,Server分配的,唯一標(biāo)識一個和server建立session的client
- 字符串+字符串的長度,唯一標(biāo)識client中的一個進(jìn)程
- client標(biāo)識+進(jìn)程標(biāo)識宗兼,唯一確定一個lock的owner躏鱼。
3.1 Linux Kernel中的對Lock Owner的設(shè)置
Lock owner大小20字節(jié):
- 8字節(jié)的固定字符,"lock id:"
- 4字節(jié)的s_dev殷绍,表示是本地哪個文件系統(tǒng)染苛。
- 8字節(jié)的id。由ida_simple_get函數(shù)創(chuàng)建主到,在本地文件系統(tǒng)中唯一茶行。對于flock,且open fd相同登钥,會復(fù)用同一個畔师。對于posix lock,且進(jìn)程相同怔鳖,也會復(fù)用同一個茉唉。
- 以上三點(diǎn),保證了在一個client內(nèi)Lock owner是唯一的结执。
- 再加上clientid度陆,可以保證所有和Server建立session連接的client來自的lock owner是唯一確定。
static void encode_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner)
{
p = reserve_space(xdr, 32);
p = xdr_encode_hyper(p, lowner->clientid);
*p++ = cpu_to_be32(20); //lock owner大小,20
p = xdr_encode_opaque_fixed(p, "lock id:", 8); //8字節(jié)
*p++ = cpu_to_be32(lowner->s_dev);//4字節(jié)
xdr_encode_hyper(p, lowner->id);//8字節(jié),不同進(jìn)程會會分配到不同id
}
4. Linux Kernel對NFS Lock實(shí)體的描述
Linux Kernel對lock實(shí)體的表示献幔,用的是struct nfs4_lock_state
懂傀。
struct nfs4_lock_state {
struct list_head ls_locks; //將同一個open file的lock插入到state->lock_states
struct nfs4_state * ls_state; //指向open state
unsigned long ls_flags;
struct nfs_seqid_counter ls_seqid;//里面重要的owner_id,由ida_simple_get函數(shù)創(chuàng)建
nfs4_stateid ls_stateid; //server端返回的信息蜡感,Server端對這個實(shí)體的key
atomic_t ls_count;
fl_owner_t ls_owner; //對于同一個文件的lock,如果此項相同會復(fù)用以前
};
4.1 nfs4_lock_state的構(gòu)造
- 輸入 state : 代表一個打開的文件
- 輸入 owner : 對于flock是open fd,對于posix是current->files
- 輸出 : 返回一個
nfs4_lock_state
結(jié)構(gòu)
struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner);
- 這個打開的文件里蹬蚁,可以獲得一個list,包含所有對這個文件lock過的owner。如果這個owner已經(jīng)在列表里郑兴,則直接返回犀斋。
- 如果沒找到,從內(nèi)存里獲取一個情连。最重要是為這個結(jié)構(gòu)叽粹,分配一個
lsp->ls_seqid.owner_id
,它是通過ida_simple_get分配的却舀。 - nfs4_proc_getlk(),nfs4_proc_unlck(),nfs4_proc_setlk()都會調(diào)用nfs4_set_lock_state()虫几,也就是為它們設(shè)置了owner。
nfs4_get_lock_state()就是根據(jù)open file state + owner唯一產(chǎn)生一個nfs4_lock_state
,它其實(shí)存在open state的一個list里挽拔,保存所有的lock state辆脸。
nfs4_set_lock_state()將nfs4_lock_state
存在了fl->fl_u.nfs4_fl.owner
,其中fl是file_lock
nfs4_proc_getlk(),nfs4_proc_unlck(),nfs4_proc_setlk()都會調(diào)用nfs4_set_lock_state()螃诅,其中nfs4_proc_setlk()會將server返回的lock stateid存在nfs4_lock_state
啡氢。而nfs4_proc_getlk()和nfs4_proc_unlck()會取出nfs4_lock_state
,從而得到lock stateid术裸,發(fā)送給server空执。
4.2 對 nfs4_lock_state的修改
NFS lock請求結(jié)束后,進(jìn)入nfs4_lock_done
,會把返回的stateid
更新到nfs4_lock_state里穗椅。
5. NFS協(xié)議中Lock的使用
對于一個實(shí)體第一次使用Lock辨绊,client需要提供: (代碼參看encode_lock
)
- open_seqid
- open_stateid
- lock_seqid
- lock_owner
Server端會返回lock stateid,以后再操作時候匹表,就直接提供lock stateid就可以了 - lock_stateid
- lock_seqid
6. Blocking lock vs Non-blocking lock
fcntl
系統(tǒng)調(diào)用會調(diào)到nfs4_proc_lock
這里
- 調(diào)用nfs4_proc_setlk()發(fā)送LOCK RPC給server
- 如果返回值是
EAGAIN
或者是non-blocking的lock(用IS_SETLK判斷)门坷,則直接返回 - 調(diào)用nfs4_set_lock_task_retry等待一個timeout時間。第一次timeout是1s,第二次是2s,最大值是30s
- 跳到步驟1
6. 為什么兩次flock不會引起第二次NFS請求
例如下面的命令袍镀,執(zhí)行兩次默蚌。第一次會導(dǎo)致發(fā)生NFS請求,但第二次不會導(dǎo)致發(fā)生NFS請求苇羡。為什么?
flock -nx /mnt/vfs/1.txt /root/test.sh
- 第一次調(diào)用時绸吸,發(fā)送完NFS請求,會調(diào)用nfs4_lock_done,經(jīng)過一些列調(diào)用會走到flock_lock_inode锦茁。注意此時equest->fl_flags是帶著FL_ACCESS標(biāo)記的攘轩,因此會將file_lock復(fù)制一份,并插入隊列码俩。
- 第二次調(diào)用時,在發(fā)送NFS請求前度帮,會再次調(diào)用flock_lock_inode。此時equest->fl_flags是不帶FL_ACCESS稿存,如果發(fā)現(xiàn)沖突笨篷,則直接返回-EAGAIN,而不會調(diào)用NFS請求瓣履。
7. call stack
fcntl64
fcntl_setlk64
flock64_to_posix_lock //fl->fl_owner = current->files
do_lock_file_wait
vfs_lock_file
nfs_lock // f_op->lock
do_setlk
nfs4_proc_lock // NFS_PROTO(inode)->lock
flock
flock_make_lock //fl->fl_owner = filp
nfs_flock // f_op->flock
do_setlk
nfs4_proc_lock // NFS_PROTO(inode)->lock
fcntl64和flock最終都調(diào)用nfs4_proc_lock
nfs4_proc_lock
nfs4_proc_setlk
_nfs4_proc_setlk
nfs4_set_lock_state
nfs4_get_lock_state
__nfs4_find_lock_state
nfs4_alloc_lock_state //set lsp->ls_seqid.owner_id
do_vfs_lock
locks_lock_inode_wait
flock_lock_inode_wait
flock_lock_inode
_nfs4_do_setlk
=> nfs4_lock_done
do_vfs_lock
nfs4_alloc_lockdata //p->arg.lock_owner.id = lsp->ls_seqid.owner_id;
8. 容易誤解的地方
- Client打開一個文件率翅,就會產(chǎn)生一個Open Owner,這個Open Owner里會有一個Open State。
- Client Lock一個文件袖迎,就會產(chǎn)生有一個Lock Owner(它一定是被打開的安聘,所以一定有一個Open Owner和一個Open state), 對這個文件不停的修改range lock的范圍,總對應(yīng)一個lock state瓢棒,而不是多個!! 所以一般情況下一個Lock Owner下浴韭,只有一個Lock State。對于delegation和layout脯宿,也被認(rèn)為是lock state念颈。這些所有的lock state存在lock owner的一個list里。對于一般情況连霉,這個list只有一個元素(就是那個lock state)榴芳。