📄 048_fs_locks_c.html
字号:
@page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_71f49z6wdg:331"> <div align=center id=bte9>
<table align=center border=0 cellpadding=0 cellspacing=0 height=5716 id=f4h2 width=800>
<tbody id=lzoi>
<tr id=sj:o>
<td height=5716 id=n5md valign=top width=100%>
<pre id=sfdi>2008-1-16<br id=vudn><br id=n_4-> 在fcntl.c的分析中已经对file lock做了一个概要性质的介绍. 这里简单回顾下. <font color=#3333ff id=use5>linux有两种文件加锁的系统调用:flock, fcntl<br id=r67d>(lockf就是fcntl). <br id=youx> fcntl默认是</font><font color=#3333ff id=ktfn><span id=ovh3 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">Advisory lock. </span><span id=rm22 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">Mandatory lock需要以特殊的方式安装文</span></font><font color=#3333ff id=m4gk><span id=aqpj style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">件系统然</span></font><font color=#3333ff id=f5fh><span id=hd79 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">后再把文件的属性改为:</span></font><span id=n53b style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=yekf style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=c65e>disabling group <br id=z1fy>execute + enabling the set-group-ID. 然后使用fcntl加锁后就是Mandatory lock了.<br id=yy1n> </font></span></span><span id=ju_- style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=l7e6 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=o2zk><font color=#000000 id=nfta>flock只容许对整个文件进行加锁,是BSD风格的,和fcntl实现的posix锁都是</font></font></span></span>advisory锁.<span id=cvuc style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=v5zo style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=vglz><font color=#000000 id=td52>而在在linux内部,posix锁,flock锁和文件<br id=sk0g>lease都是使用 flle_lock来实现的.在浏览这个文件之前,先复习下fcntl这个重要接口:<br id=e_dq><br id=rgvh></font></font></span></span>static long <font color=#000099 id=v3:j><b id=kyfe>do_fcntl</b></font>(unsigned int fd, unsigned int cmd,<br id=np1n> unsigned long arg, struct file * filp)<br id=x-2l>{<br id=vzrz> switch (cmd) {<br id=egnb> .....<br id=r6s:> <b id=jgiy>case F_GETLK</b>: <font color=#3333ff id=yuol><b id=hzpp>/*Posix Lock 操作*/</b></font><br id=dg7-> err = fcntl_getlk(fd, (struct flock *) arg);<br id=ycrd> break;<br id=wo4u> <b id=tz.8>case F_SETLK</b>:<br id=o9jh> <b id=zgc3>case F_SETLKW</b>:<br id=scxh> err = fcntl_setlk(fd, cmd, (struct flock *) arg);<br id=yjk9> break;<br id=azmk> case<font color=#3333ff id=c3j6> F_GETOWN</font>: <font color=#3333ff id=xr3_>/*分析dnotify.c的时候说过,owener用于记录当文件发生变化<br id=tm02> 需要通知的进程, owener在不同的情景中有不同的用法:<br id=trac> <font color=#cc0000 id=er:k> 1. dir notify 2.lease(via fl->fl_fasync) <br id=ymbg> 3. fasync (via specific fasync queue)</font><br id=gd2_> */</font><br id=cdbj> err = filp->f_owner.pid;<br id=tihb> break;<br id=apol> case<font color=#3333ff id=gymr> F_SETOWN:</font><br id=upuj> lock_kernel();<br id=xbp6> filp->f_owner.pid = arg;<br id=d86d> filp->f_owner.uid = current->uid;<br id=nh4q> filp->f_owner.euid = current->euid;<br id=gzfv> err = 0;<br id=b3:2> if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))<br id=plh7> err = sock_fcntl (filp, F_SETOWN, arg);<br id=s59b> unlock_kernel();<br id=w9jx> break;<br id=jluc> case <font color=#3333ff id=a3fs>F_GETSIG:</font><br id=d.dd> err = filp->f_owner.signum;<br id=tj4y> break;<br id=j7u3> case <font color=#3333ff id=g28b>F_SETSIG</font>:<br id=q5su> /* arg == 0 restores default behaviour. */<br id=x4vh> if (arg < 0 || arg > _NSIG) {<br id=jb-s> break;<br id=ukxn> }<br id=n8mr> err = 0;<br id=w16q> filp->f_owner.signum = arg;<br id=vps_> break;<br id=tnh-> <font color=#cc33cc id=yvm4>case F_GETLEASE:</font><br id=fquq> err = fcntl_getlease(filp);<br id=na:d> break;<br id=vc0r> <font color=#cc33cc id=yx8.>case F_SETLEASE:</font><br id=hv:_> err = fcntl_setlease(fd, filp, arg);<br id=icy2> break;<br id=ne-q> <font color=#336666 id=ch50>case F_NOTIFY: /*dir notify 通知*/</font><br id=fxqo> err = fcntl_dirnotify(fd, filp, arg);<br id=l415> break;<br id=vskk> ...<br id=bw4:>}<br id=pidg><span id=imre style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=p9zt style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=p8bi><font color=#000000 id=dy7j><br id=gw01>然后看文件的全部函数,可以将其分成几个功能部分:<br id=reyg><br id=er6t>0) </font></font></span></span><span id=vjfe style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=cg-a style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">struct <font color=#000099 id=s3mv><b id=d28b>file_lock</b></font> 的管理<br id=ss.2></span></span><span id=cnex style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=sc9w style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">struct <font color=#000099 id=z:_f><b id=tauw>file_lock</b></font> {<br id=gkjg> struct file_lock *fl_next; /* singly linked list for this inode:<font color=#3333ff id=n.90>file_lock 在inode上配置</font>*/<br id=hjhj> struct list_head fl_link; /* <font color=#3333ff id=a:78>接入全局链表file_lock_list,lease或者file_lock:正在使用</font>*/<br id=l74s> struct list_head fl_block; /* <font color=#3333ff id=u3qh>circular list of blocked processes:被block的file_lock(file_lock-><br id=sm3y> fl_wait才是那个进程)</font> (</span></span><font color=#3333ff id=swym><span id=k:xe style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">Mandatory or flock)</span></font><span id=lmie style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=bu9h style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">*/<br id=no9z> fl_owner_t fl_owner; /*typedef struct files_struct *fl_owner_t;对文件加锁的进程之文件系统接口 */<br id=ucsy> unsigned int fl_pid; /*对文件加锁的进程的pid*/<br id=hi2w> wait_queue_head_t fl_wait; /*锁的等待队列:</span></span><font color=#3333ff id=rvx:><span id=aqad style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">Mandatory 和flock使用,</span></font><span id=inm1 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=i_qw style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">*/<br id=vq6l> struct file *fl_file;/*被加锁的文件*/<br id=fl_y> unsigned char fl_flags;/* <font color=#cc0000 id=rb1c>FL_POSIX</font> <font color=#ff0000 id=y4bk>FL_FLOCK</font> <strike id=dot8>FL_BROKEN</strike> FL_ACCESS <strike id=bn:r>FL_LOCKD</strike> <font color=#ff0000 id=pu3y>FL_LEASE</font>*/<br id=weuu> unsigned char fl_type; /* <font color=#cc0000 id=seuh>F_RDLC F_WRLCK F_UNLCK</font> */<br id=wu41> loff_t fl_start;<br id=up95> loff_t fl_end;<br id=mb2.> <font color=#3333ff id=r7e0> /*下面三个没啥用, 都是NULL*/</font><br id=hzh6> void (*fl_notify)(struct file_lock *); /* unblock callback */ <br id=c:ei> void (*fl_insert)(struct file_lock *); /* lock insertion callback */<br id=igse> void (*fl_remove)(struct file_lock *); /* lock removal callback */<br id=dtsi><br id=p3hl> struct fasync_struct * fl_fasync; /* for <font color=#cc0000 id=jike>lease</font> break notifications */<br id=j3bz><br id=isfl> union {<br id=blt.> struct nfs_lock_info nfs_fl;<br id=c:-s> } fl_u;<br id=m9vp>};<br id=ilsh>管理比较简单,就是free alloc而已,没有像dentry和inode那样的cache机制了(因为也不把flock写到磁盘的...).再说下file_lock<br id=qsox>的类别: fl_flags指出是那种类型的锁:posix,lease,flock三种,还有access,access是一个临时锁,用于加锁的时候发现有冲突时把<br id=lkxv>access类型的锁(总是posix类型的)挂入冲突链表,而进程是wait在这个access类型的锁的fl_wait上(</span></span><font color=#3333ff id=z22a><span id=q8uc style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">Mandatory </span></font><span id=saq8 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=k.nt style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">).(<font color=#000099 id=wd0_>这个设计需要注<br id=h.vy>意</font>)<br id=bxli>static struct file_lock *<font color=#000066 id=jv-f><b id=v2tv>locks_alloc_lock</b></font>(int account)<br id=r82.>static inline void <font color=#000066 id=lmzr><b id=kxkk>locks_free_lock</b></font>(struct file_lock *fl)<br id=qzrb>void <font color=#000099 id=c3wy><b id=j-vt>locks_init_lock(</b></font>struct file_lock *fl)<br id=qz5v>void <font color=#000099 id=a8ja><b id=uxl1>locks_copy_lock</b></font>(struct file_lock *new, struct file_lock *fl)<br id=w3nz><br id=nfzq></span></span><span id=l.cu style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=j0my style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=kowh><font color=#000000 id=ro9b>1)Advisoy lock/ Mandatory lock 的实现<br id=ky8u>上面说过advisoy lock有:fcntl实现的posix锁和flock实现的BSD风格的锁. Mandatory锁是posix锁的一种加强形式.开启方式上面也<br id=ls.n>都说过了. 下面几个函数是比较简单几个:<br id=pg93><br id=thxz>static int <font color=#000066 id=xyer><b id=s3o1>assign_type</b></font>(struct file_lock *fl, int type)/*处理fl_type:</font></font></span></span><span id=j57l style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=wy-: style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"> <font color=#cc0000 id=yrn9>F_RDLC F_WRLCK F_UNLCK</font></span></span><span id=wrqc style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=n7oc style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=r9oh><font color=#000000 id=xrmu>*/<br id=xynx>static int <font color=#000066 id=mx5h><b id=qf75>flock_to_posix_lock</b></font>(struct file *filp, struct file_lock *fl, struct flock *l)/*把用户传递的l转换成fl*/<br id=zxg5>static int <font color=#000099 id=g5:8><b id=kuuv>flock64_to_posix_lock</b></font>(struct file *filp, struct file_lock *fl,...)/*同上*/<br id=e92v>另外以部分函数实现锁检测,判断是否已经加锁,有没有冲突.从大的模块看去,posix lock, mandatory lock,和flock各有一个函数用<br id=x2bt>于判定锁是否冲突:<br id=ctgk>/*<font color=#cc0000 id=zlz4>这个函数用于posix advisory lock的冲突判定</font>,主要caller:<font color=#000099 id=xh_.><b id=wxjs>posix_lock_file</b></font>,<font color=#000099 id=clz_><b id=oq1w>posix_test_lock</b></font> */<br id=ipa3>static int <font color=#3333ff id=ce-2><b id=oth_>posix_locks_conflict</b></font>(struct file_lock *caller_fl, struct file_lock *sys_fl)<br id=j4fj>{<br id=pe6b> /* 某一个进程加的锁不会锁定自己*/<br id=jox3> if (!(sys_fl->fl_flags & FL_POSIX) ||<br id=r_t0> <font color=#006600 id=ijof><b id=o3hy>locks_same_owner</b></font>(caller_fl, sys_fl))<br id=xyrt> return (0);<br id=stoh> /* Check whether they overlap */<br id=vdb8> if (!<font color=#006600 id=c5ik><b id=buad>locks_overlap</b></font>(caller_fl, sys_fl)) <font color=#3333ff id=ifb9>/*如果没有overlap也不冲突(锁定区域不同)*/</font><br id=e7op> return 0;<br id=etat> return (<font color=#006600 id=hf18><b id=nvbm>locks_conflict</b></font>(caller_fl, sys_fl)); <font color=#000099 id=c8s.>/*reader不会block reader, writer要block 所有 .....*/</font><br id=uu15>}<br id=rrdz><br id=u321>/*flock 的冲突判定: 唯一caller,<font color=#000099 id=e1gb><b id=lbcs>flock_lock_file</b></font>*/<br id=en8b>static int <font color=#3333ff id=th3n><b id=ijks>flock_locks_conflict</b></font>(struct file_lock *caller_fl, struct file_lock *sys_fl)<br id=mj9i>{<br id=gohs> <font color=#3333ff id=v:fq>/* flock对同一个filep(比如dup会使用同一个fl_file,即struct file*)不会冲突*/</font><br id=l2yq> if (!(sys_fl->fl_flags & FL_FLOCK) ||(caller_fl->fl_file == sys_fl->fl_file))<br id=buoi> return (0);<br id=oa5e>#ifdef MSNFS<br id=khvu> if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))<br id=ssos> return 0;<br id=re2_>#endif<br id=mszl> return (<font color=#006600 id=ukq9><b id=lhze>locks_conflict</b></font>(caller_fl, sys_fl)); /*已经看过了*/<br id=yi8y>}<br id=mg-u>/*mandatory lock的冲突判定,用于读写文件时判定mandatory lock,主要caller:<font color=#000099 id=mfcv><b id=pw9w>locks_verify_area</b></font>*/<br id=m-xr>/*只有mandatory性质的锁才会在检测的时候同时等待:强制执行,而flock和posix lock只是加锁的时候才必须等待(读写文件,<br id=il1c>getlk都不用等的*/<br id=soof>int <font color=#3333ff id=l2pu><b id=p410>locks_mandatory_area</b></font>(int read_write, struct inode *inode,struct file *filp, loff_t offset,size_t count)<br id=aj1b>{<br id=lhy4> struct file_lock *fl;<br id=x3mz> struct file_lock *new_fl = locks_alloc_lock(0);<br id=qla-> int error;<br id=m42t>/*创建一个access锁,*/<br id=dhzt>....<br id=pt8r> new_fl->fl_flags =<font color=#990000 id=mcto> <font color=#cc0000 id=k6vj>FL_POSIX | FL_ACCESS</font></font><font color=#cc0000 id=idn0>;</font><br id=kjkl> new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;<br id=vn-0>....<br id=s8l5> error = 0;<br id=iup1> lock_kernel();<br id=hvoh>repeat:<br id=trtx> /* 找到冲突的lock,然后block到这个file_lock上:进程wait在new_fl->fl_wait,new_fl挂入fl->fl_block链表 */<br id=nkih> for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {<br id=o-hv> if (!(fl->fl_flags & FL_POSIX))<br id=d9.k> continue;<br id=j8jz> if (fl->fl_start > new_fl->fl_end)<br id=o:p7> break;<br id=er_g> if (<font color=#006600 id=ypw1><b id=kx:n>posix_locks_conflict</b></font>(new_fl, fl)) {<br id=s9om> error = -EAGAIN;<br id=vc45> if (filp && (filp->f_flags & O_NONBLOCK))<br id=x:8o> break;<br id=nizg> error = -EDEADLK;<br id=v4y6> if (<font color=#006600 id=z_qm><b id=swvt>posix_locks_deadlock</b></font>(new_fl, fl)) <font color=#3333ff id=gtj1>/*检测死锁...*/</font><br id=ilmt> break;<br id=f:ve> <font color=#3333ff id=lk7g>/*block on操作同样用于flock和file lease*/<br id=s2cv></font></font></font></span></span><span id=x4w0 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=hho5 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=kxx_><font color=#000000 id=q__:><font color=#3333ff id=fwaz> /*</font></font></font></span></span><font color=#3333ff id=wjpv><span id=wnpx style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=c9tu style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal">进程wait在new_fl->fl_wait,new_fl(access)挂入fl->fl_block链表</span></span></font><span id=ecvl style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=jwfw style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=a5x1><font color=#000000 id=v5:z><font color=#3333ff id=jpd4>*/</font></font></font></span></span><span id=t_bz style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=h-6o style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=lfg2><font color=#000000 id=z8x_><font color=#3333ff id=pfel><br id=r73h></font> error = <font color=#006600 id=a8v9><b id=rvlo>locks_block_on</b></font>(fl, new_fl); </font></font></span></span><span id=w6nr style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=u540 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=euj8><font color=#000000 id=n3q4><br id=iwav> if (error != 0)<br id=sw9w> break;<br id=mef6> /*<font color=#990000 id=bemd> 小心对待sleeping问题</font><br id=rgik> * If we've been sleeping someone might have<br id=pew6> * changed the permissions behind our back.<br id=u-l3> */<br id=vdlz> if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)<br id=hdld> break;<br id=mhz.> goto repeat;<br id=zscp> }<br id=auye> }<br id=d_5.> locks_free_lock(new_fl);<font color=#3333ff id=iw54> /*释放access file lock*/</font><br id=msa0> unlock_kernel();<br id=r1y6> return error;<br id=c0-l>}<br id=emwc>然后看看死锁避免算法:<br id=bzxq>static int posix_locks_deadlock(struct file_lock *caller_fl,<br id=n:tq> struct file_lock *block_fl)<br id=pd75>{<br id=f:vb> struct list_head *tmp;<br id=xjtd> fl_owner_t caller_owner, blocked_owner;<br id=vzn2> unsigned int caller_pid, blocked_pid;<br id=y.:d><br id=czk.> caller_owner = caller_fl->fl_owner; <font color=#000099 id=mwc9>//caller,比如是进程X试图获得此锁</font><br id=jsxd> caller_pid = caller_fl->fl_pid;<br id=ipcb> blocked_owner = block_fl->fl_owner;<font color=#000099 id=wlgu> //进程A已经获得此锁了(没有被block)</font><br id=lvt_> blocked_pid = block_fl->fl_pid;<br id=qgdc><br id=kdjp>next_task:<br id=sj8j> if (caller_owner == blocked_owner && caller_pid == blocked_pid) <font color=#3333ff id=imrs>/*如果X==A,证明A试图再次获取同一个锁,dead!*/</font><br id=m9r4> return 1;<br id=jlwe> list_for_each(tmp, &blocked_list) { <font color=#3333ff id=rdtr>/*试图获得此锁的进程不是A,那就看A是否在等待其他锁*/</font><br id=pkpb> struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);<br id=yzkm> if ((fl->fl_owner == blocked_owner)<br id=b..v> && (fl->fl_pid == blocked_pid)) {<br id=l5il> fl = fl->fl_next;<br id=c.oz> blocked_owner = fl->fl_owner; <font color=#3333ff id=u6f->/*A在等待另一个进程B的锁,则其等待进程不能是X....*/</font><br id=zn0c> blocked_pid = fl->fl_pid;<br id=l6:x> goto next_task;<br id=ykrs> }<br id=awcs> }<br id=k_pm> return 0;<br id=hdli>}<br id=wqx->出了上面提到的真正需要等带的情况,还有锁检测,只是看看有没有冲突...<br id=d63q><font color=#000099 id=dkpf><b id=kw3_>posix_test_lock</b></font>(struct file *filp, struct file_lock *fl) <font color=#3366ff id=zt-b>/*posix fcntl getlk使用,返回冲突的fl*/</font><br id=zt5.>int <font color=#000099 id=sggi><b id=o8e7>locks_mandatory_locked</b></font>(struct inode *inode) <font color=#3366ff id=e8xj>/*只看有没有锁,不看锁定哪里,shared mmap互斥用*/</font><br id=johp><br id=b:zw>当文件关闭的时候需要清理locks,这一点上posix lock和flock的清理时机是不同的(具体的函数就不列举了....):<br id=to:k>void <font color=#3333ff id=cmif><b id=w0j4>locks_remove_posix</b></font>(struct file *filp, fl_owner_t owner) <font color=#3333ff id=hnh:>/*posix lock在进程关闭时把本进程加的锁清理掉*/</font><br id=gzx->void <font color=#3333ff id=tkb2><b id=ro:i>locks_remove_flock</b></font>(struct file *filp) <font color=#3366ff id=g7sz>/*而flock只有当os释放文件本身时才会清理(比如dup了,那么原始进程close文<br id=y0ph>件的时候就不能吧flock清理掉*/</font><br id=a-xq><br id=e823>2)posix lock加锁的实现<br id=oog5>就是函数int posix_lock_file,看起来挺长挺繁琐. 这里做一个分析方式的讨论. 首先,posix lock如果是setfl的话先去处理可能的<br id=t_mv>冲突情况,并预先分配好file_lock.然后就是合并操作带来的繁琐操作,合并分两种:<br id=ixzm> I)类型相同 那么就寻找当前fl中有没有邻接或者包含,只有这种情况才能合并.<br id=a5.z> I)类型不同 只能合并overlap的fl,newfl把当前的分成两个部分,或者newfl从左边或者从右边覆盖当前fl<br id=ndnh>分析的时候记着画张图,就简单多了. 具体代码就不列举了.<br id=obbm>下面的函数也不再分析或列举.<br id=y2xo>int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)<br id=vnp1>int fcntl_getlk(unsigned int fd, struct flock *l)<br id=i7b1>static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)<br id=sxnz><br id=kclp>注: locks_block_on 和 locks_wake_up_blocks是一对相互呼应的函数.file_lock的等待是这样实现的:(以mandatory为例)<br id=iqj9>分配一个新的file_lock,比如newfl,(mandatory lock将其标记为access类型),如果有冲突发生,将newfl接入冲突的fl值fl_block队<br id=qk6i>列,然后进程是wait在newfl->fl_wait之上的.<br id=qce9><br id=n7-_></font></font></span></span><span id=jq.t style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=vnxk style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=vriw><font color=#000000 id=u6vc>3)flock<br id=wpl.></font></font></span></span><span id=j1kf style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=u747 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=xfxl><font color=#000000 id=ksuu>flock和posix lock都共享一组机制.看完posix的lock再看flock也就比较容易理解.<br id=c8xj>static inline int <font color=#000099 id=v_mz><b id=w6rz>flock_translate_cmd</b></font>(int cmd) /*simple*/<br id=j380></font></font></span></span><span id=bfis style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=a70t style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=nz0l><font color=#000000 id=h:bx>static struct file_lock *<font color=#cc0000 id=pk5f><b id=gx_h>flock_make_lock</b></font>(struct file *filp, unsigned int type) /*只针对整个文件*/</font></font></span></span><br id=tot3><span id=c.s. style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=uqom style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=o1ty><font color=#000000 id=xo3t>asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)/*系统调用接口,*/<br id=ktmn>加锁的过程:(把filep想成是加锁者吧,好理解些)<br id=df2m> 1)如果是unlock,就找到同一个的filep的flock,删除即可. (想象是'filep'发起的lock,一个进程open两次同一个file...)<br id=x1hd> 2)如果是相同的锁, read对read可以成功, r 对w,或者w对w就冲突,见</font></font></span></span><span id=ctaf style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=i2gk style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=x6x2><font color=#000000 id=r0.f><span id=s.je style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=m2fc style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=kz0x><font color=#000000 id=cucb><font color=#009900 id=l608><b id=uoev>flock_locks_conflict.<br id=h_60></b></font></font></font></span></span> 3)对同一个filep的flock可能有多个,比如都是read(多次open),</font></font></span></span><span id=mhsp style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=ppr. style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=qldy><font color=#000000 id=vi3l>如果要加write类型的锁</font></font></span></span><span id=ena7 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=rezb style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=ig_7><font color=#000000 id=h2d0>可能需要等待多个flock撤销<br id=njz1><br id=sbqo>static int <font color=#3333ff id=yz39><b id=lmhe>flock_lock_file</b></font>(struct file *filp, unsigned int lock_type,unsigned int wait)/*核心函数*/<br id=fixk>{<br id=fhs8> struct file_lock *fl;<br id=l17x> struct file_lock *new_fl = NULL; /*为了使用和posix lock一样的wait机制,总是试图file_lock*/<br id=worj> struct file_lock **before;<br id=hp7e> struct inode * inode = filp->f_dentry->d_inode;<br id=fcqd> int error, change;<br id=yq4b> int unlock = (lock_type == F_UNLCK);<br id=wj94><br id=xr8e> /*<br id=tj2.> * If we need a new lock, get it in advance to avoid races.<br id=qa:-> */<br id=cbi1> if (!unlock) { <br id=cwmz> error = -ENOLCK;<br id=npip> new_fl =<font color=#009900 id=n-gb><b id=zev_> flock_make_lock</b></font>(filp, lock_type); /*预分配*/<br id=g_sd> if (!new_fl)<br id=toi4> return error;<br id=dpwj> }<br id=nv_p><br id=ig7i> error = 0;<br id=nae->search: <font color=#3333ff id=f52c>/*search这段保证一个filep只有一个flock存在...*/</font><br id=ppra> change = 0;<br id=bdhs> before = &inode->i_flock;<br id=gm27> while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {<br id=kgqr> if (filp == fl->fl_file) {<font color=#cc0000 id=jxv9>/*flock认为是一个filp(而不是进程)来进行加锁*/ </font><br id=evno> if (lock_type == fl->fl_type) /*</font></font></span></span><span id=k1.d style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=h8u- style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=hoku><font color=#000000 id=u1w_><font color=#cc0000 id=ptnw>,如果对dup的fd进行两次加ex锁,那就都会成功....注意了....</font></font></font></span></span><span id=irtk style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=ul11 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=o4_t><font color=#000000 id=xo3b>*/<br id=j9v6> goto out;<br id=u5yo> change = 1;<br id=ho7h> break;<br id=irzg> }<br id=gk9o> before = &fl->fl_next;<br id=q79p> }<br id=tvcg> /* change means that we are changing the type of an existing lock,<br id=h_bh> * or else unlocking it.<br id=kwc2> */<br id=xlns> if (change) {<br id=pc_a> /* N.B. What if the wait argument is false? */<br id=udn1> <font color=#009900 id=zwjp> <b id=gw98>locks_delete_lock</b>(</font>before, !unlock);<font color=#3333ff id=y8mm>/*对同一个filp进行不同种类的加锁,会修改现有的锁类型..注意了....*/</font><br id=p72y><font color=#3333ff id=adnm> /*<br id=ymue> * If we waited, another lock may have been added ...<br id=j60z> */</font><br id=z6qo> if (!unlock)<br id=i2cb> goto search;<br id=accx> }<br id=ybfq> if (unlock)<br id=j76o> goto out;<br id=feq2><br id=pdqj>repeat: <font color=#000099 id=adwz>/*这段保证不同的filep之间如果冲突了,就要进行互斥*/</font><br id=rmk7> for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);<br id=kf04> fl = fl->fl_next) {</font></font></span></span><span id=tz3t style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=m4h. style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=hhmq><font color=#000000 id=l6ve><font color=#3333ff id=bpza>/*flock应用在inode上(物理文件上)*/</font></font></font></span></span><span id=e8.j style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=z_yj style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=zrnn><font color=#000000 id=a7-6><br id=pxzb> if (!<font color=#006600 id=ou3w><b id=rybe>flock_locks_conflict</b></font>(new_fl, fl))<br id=qasc> continue;<br id=q0jn> error = -EAGAIN;<br id=y8sc> if (!wait)<br id=mb22> goto out;<br id=le0x> error = <font color=#006600 id=kfvz><b id=utc5>locks_block_on</b></font>(fl, new_fl);<br id=rxgv> if (error != 0)<br id=v:6x> goto out;<br id=ofu1> goto repeat;<br id=jn4z> }<br id=w.s-> <font color=#006600 id=yw11><b id=muku>locks_insert_lock</b></font>(&inode->i_flock, new_fl);<br id=jl26> new_fl = NULL;<br id=m03d> error = 0;<br id=xc_g><br id=s42y>out:<br id=u7lw> if (new_fl)<br id=fa7i> locks_free_lock(new_fl);<br id=q:t2> return error;<br id=u:k5>}<br id=qzqf><br id=zda:>3)file lease<br id=myll>还是看看man怎么说:<br id=c_1y></font></font></span></span>A <span class=highlight2 id=m70d>file</span> <span class=highlight3 id=dood>lease</span> provides a mechanism whereby the process holding the <span class=highlight3 id=howu>lease</span> (the "<span class=highlight3 id=cb2i>lease</span> holder") is notified <br id=r.8y>(via delivery of a signal) when a process (the "<span class=highlight3 id=w6j4>lease</span> breaker") tries to <i id=yzdm><b id=zmci><a href=http://linux.die.net/man/2/open id=wtes rel=nofollow>open</a></b>(2)</i> or <i id=bk63><b id=rs70><a href=http://linux.die.net/man/2/truncate id=wan9>truncate</a></b>(2)</i> that <span class=highlight2 id=zr3m>file</span>.<br id=l._3><span id=ys0s style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=tsua style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=l0_n><font color=#000000 id=nhpx><br id=e:cf>我能找到的例子是</font></font></span></span> Samba 使用了file lease 来实现一种opportunistic locks (oplocks).具体的file lease也是一个比较隐晦的操作:<br id=ulc.>hold file lease的进程需要主动降级或者删除lease,否则内核会强制的降级或者删除其lease.不知道这样是什么道理....<br id=dmkj>另外看似如果在一个文件上使用了lease,就不能再有flock或者posix lock了(有冲突...很奇怪,file lease的代码假定lease类型的锁必须在<br id=kkdp>inode->i_flock的第一个上,否则就会有问题....<font color=#3333ff id=t_a4> 好在,linux2.6修正了这个问题</font>...).<br id=q9dc><br id=c9-u><br id=wgzr><span id=jf05 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=xn:3 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=bjk5><font color=#000000 id=r7a2>static int <font color=#000099 id=eq7c><b id=fhnv>lease_alloc</b></font>(struct file *filp, int type, struct file_lock **flp) /*一个类型为FL_LEASE的file lock*/<br id=ubvn>int fcntl_getlease(struct file *filp)<br id=jy8t>{<br id=v-5u> struct file_lock *fl;<br id=pxfp> <br id=mu:d> fl = filp->f_dentry->d_inode->i_flock; <font color=#3333ff id=eyoi>/*从这里看出lease有重大缺陷....linux2.6已经完全不同了...*/</font><br id=widb> if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0))<br id=x9qp> return F_UNLCK;<br id=sm7e> return fl->fl_type & ~F_INPROGRESS;<br id=uwpu>}<br id=bt2:>get_lease, linux2.6中更名为 _break_lease,更容易理解些..,这个函数是在open和truncate的时候进行的租约判定:<br id=tw8u>int __get_lease(struct inode *inode, unsigned int mode)<br id=medk>{<br id=j11d> int error = 0, future;<br id=ipwl> struct file_lock *new_fl, *flock;<br id=gco:> struct file_lock *fl;<br id=tofy> int alloc_err;<br id=mw:c><br id=v:sw> alloc_err = <font color=#006600 id=r06w><b id=ji:d>lease_alloc</b></font>(NULL, 0, &new_fl); <font color=#3333ff id=j.en>/*类似posix lock 的access,总之用同样的代码...*/</font><br id=w9t6><br id=p947> lock_kernel();<br id=r530> flock = inode->i_flock; <font color=#3333ff id=wfrh>/*认为第一个floc就是FL_LEASE...!!! 2.6中已经修正了.(或者我想错了?!)*/</font><br id=d9q:> if (flock->fl_type & F_INPROGRESS) { <font color=#3333ff id=cdxr>/*等待lease的holder解除/降级lease*/</font><br id=lnh:> if ((mode & O_NONBLOCK)<br id=v7yh> || (flock->fl_owner == current->files)) { <br id=pv3q> /*NONBLOCK好理解,但是自己拥有这个lease的情况约定返回would block?*/<br id=n9tr> <font color=#3333ff id=ejn1>/*如果是自己拥有这个lease,就返回would block(RequestComments)*/</font><br id=h30z> error = -EWOULDBLOCK;<br id=jxw9> goto out;<br id=zcqj> }<br id=u7q4> if (alloc_err != 0) {<br id=da1z> error = alloc_err;<br id=dlf3> goto out;<br id=oi1b> }<br id=dh23> do {<br id=r.eh> error = <font color=#006600 id=aj_r><b id=n_8o>locks_block_on</b></font>(flock, new_fl); <font color=#3333ff id=jyr_>/*F_INPROGRESS的情况,等待所有的lease解除*/</font><br id=ikd.> if (error != 0)<br id=tyk1> goto out;<br id=kqvi> flock = inode->i_flock;<br id=j8k2> if (!(flock && (flock->fl_flags & FL_LEASE)))<font color=#3333ff id=o8b:>/*2.6干净许多,这个操作很tricky*/</font><br id=o4vp> goto out;<br id=ywpa> } while (flock->fl_type & F_INPROGRESS);<br id=e3wz> }<br id=jnwg> <font color=#3333ff id=yrrm>/*根据要求的操作,决定lease是降级还是解锁...*/</font><br id=zx_e> if (mode & FMODE_WRITE) {<br id=hvcu> /* If we want write access, we have to revoke any lease. */<br id=l7xg> future = F_UNLCK | F_INPROGRESS;<br id=qyze> } else if (flock->fl_type & F_WRLCK) {<br id=t7t_> /* Downgrade the exclusive lease to a read-only lease. */<br id=g0dp> future = F_RDLCK | F_INPROGRESS;<br id=dhrp> } else {<br id=sp6e> /* the existing lease was read-only, so we can read too. */<br id=izto> goto out;<br id=pgl_> }<br id=e5f1><br id=y_8t> if (alloc_err && (flock->fl_owner != current->files)) {<br id=y9-k> error = alloc_err;<br id=ngwo> goto out;<br id=d9jt> }<br id=qvlp> <font color=#3333ff id=fki1> /*降级lease*/</font><br id=u8jc> fl = flock;<br id=lflc> do {<br id=u9vz> fl->fl_type = future;<br id=w0mz> fl = fl->fl_next;<br id=cn_f> } while (fl != NULL && (fl->fl_flags & FL_LEASE));<br id=mn1k><br id=wew7> <font color=#006600 id=j74q><b id=l1.n>kill_fasync</b></font>(&flock->fl_fasync, SIGIO, POLL_MSG); <font color=#3333ff id=a7y1>/*通知lease的holder,参考fcntl对fasync的分析*/</font><br id=pc98><br id=tj7h> if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) {<br id=w:ix> error = -EWOULDBLOCK;<br id=o1ck> goto out;<br id=hnf:> }<br id=ofir><br id=aijz> if (lease_break_time > 0)<br id=a3he> error = lease_break_time * HZ;<br id=qldd> else<br id=hfdt> error = 0;<br id=al_a>restart:<br id=fk38> error = locks_block_on_timeout(flock, new_fl, error); <font color=#000099 id=nbja>/*等待降级!*/</font><br id=j8cv> if (error == 0) {<br id=j8z6> /* We timed out. Unilaterally break the lease. */<br id=h_tr> <font color=#006600 id=odia><b id=lidw>locks_delete_lock</b></font>(&inode->i_flock, 0); <font color=#000099 id=fabq>/*超时,强制降级*/</font><br id=vt6o> printk(KERN_WARNING "lease timed out\n");<br id=gef:> } else if (error > 0) {<br id=bkif> flock = inode->i_flock;<br id=foia> if (flock && (flock->fl_flags & FL_LEASE))<br id=a0wf> goto restart;<br id=f67-> error = 0;<br id=bfpr> }<br id=zd76><br id=dx:f>out:<br id=qekn> unlock_kernel();<br id=sg06> if (!alloc_err)<br id=fgz4> locks_free_lock(new_fl);<br id=hdaa> return error;<br id=mjr0>}<br id=nv72>然后,关于lease,还有一个函数:<br id=dr0j>int fcntl_setlease(unsigned int fd, struct file *filp, long arg)<br id=c7eh><font color=#993399 id=d7ix>强烈建议不要看这个函数了,读一下2.6的对应函数即可,这个实在不是很好.</font><br id=gtel>{<br id=mtzm> /*sanity check*/ <br id=hkmc><br id=h5vz> /*<br id=da23> * FIXME: What about F_RDLCK and files open for writing?<br id=g6bv> */<br id=m:5u> if ((arg == F_WRLCK)<font color=#663366 id=bvt3> <font color=#cc0000 id=k-ss>/*加write租约要求没有其他人已经打开这个文件(dentry,open一次计数增1)*/</font></font><br id=ix.4> && ((atomic_read(&dentry->d_count) > 1)<br id=yv1l> || (atomic_read(&inode->i_count) > 1)))<br id=oald> return -EAGAIN;<br id=xf-h><br id=wt-e> before = &inode->i_flock;<br id=jfz.><br id=j5au> lock_kernel();<br id=zn6d></font></font></span></span><span id=x2hi style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=y0d5 style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=abl6><font color=#000000 id=fwjn> <font color=#3333ff id=vmnx> /* comments in 2.6<br id=thga> * At this point, we know that if there is an exclusive<br id=hx.v> * lease on this file, then we hold it on this filp<br id=nbq1> * (otherwise our open of this file would have blocked).<br id=xe0r> * And if we are trying to acquire an exclusive lease,<br id=ek6j> * then the file is not open by anyone (including us)<br id=h5xj> * except for this filp.<br id=jgjk> */</font></font></font></span></span><br id=lq:1><span id=h:-y style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=ki1o style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=uceq><font color=#000000 id=zvhm> while ((fl = *before) != NULL) {<br id=cqdx> if (fl->fl_flags != FL_LEASE)<br id=ypfo> break;<br id=kak6> if (fl->fl_file == filp)<br id=h2pm> my_before = before; /<font color=#3333ff id=mave>*对同一个filp lease几次的话只有最后一个生效,也就是只有一个flock*/</font><br id=bq_n> else if (fl->fl_type & F_WRLCK)<br id=fijm> wrlease_count++;<br id=kta-> else<br id=zm:j> rdlease_count++;<br id=nf31> before = &fl->fl_next;<br id=xg..> }<br id=z3t8><br id=pokk> if ((arg == F_RDLCK && (wrlease_count > 0)) ||<br id=v0kx> (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) {<br id=of98> error = -EAGAIN;<br id=ebvy> goto out_unlock;<br id=ju86> }<br id=q4pn><br id=h66:> if (my_before != NULL) {</font></font></span></span><span id=n0ra style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=jfsq style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=r9-t><font color=#000000 id=da10>/<font color=#3333ff id=f9-m>*对同一个filp lease几次的话只有最后一个生效,也就是只有一个flock*/</font></font></font></span></span><span id=vm1u style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><span id=h:vk style="FONT-SIZE:10.5pt; COLOR:#000000; LINE-HEIGHT:normal"><font color=#3333ff id=anpg><font color=#000000 id=awhy><br id=s016> error = lease_modify(my_before, arg, fd, filp);<br id=fnb0> goto out_unlock;<br id=kyqb> }<br id=ph46><br id=f2v8>............<br id=stu9> error = lease_alloc(filp, arg, &fl);<br id=fkfx> if (error)<br id=u:n2> goto out_unlock;<br id=a5lp><br id=ove-> error = fasync_helper(fd, filp, 1, &fl->fl_fasync);<br id=t26v> if (error < 0) {<br id=g.se> locks_free_lock(fl);<br id=q70y> goto out_unlock;<br id=nelg> }<br id=m_5r>..........<br id=yx_l>}<br id=c.du><br id=c.d0>许有未决问题....<br id=jb7p><br id=j2d4>注:略过了对NFS的分析....<br id=z:xb><br id=pnc.></font><br id=i902><br id=k39n><br id=fofe></font></span></span></pre>
</td>
</tr>
</tbody>
</table>
</div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -