Python使用文件锁实现进程间同步功能【基于fcntl模块】
本文实例讲述了Python使用文件锁实现进程间同步功能。分享给大家供大家参考,具体如下:
简介
在实际应用中,会出现这种应用场景:希望shell下执行的脚本对某些竞争资源提供保护,避免出现冲突。本文将通过fcntl模块的文件整体上锁机制来实现这种进程间同步功能。
fcntl系统函数介绍
Linux系统提供了文件整体上锁(flock)和更细粒度的记录上锁(fcntl)功能,底层功能均可由fcntl函数实现。
首先来了解记录上锁。记录上锁是读写锁的一种扩展类型,它可用于有亲缘关系或无亲缘关系的进程间共享某个文件的读与写。被锁住的文件通过其描述字访问,执行上锁操作的函数是fcntl。这种类型的锁在内核中维护,其宿主标识为fcntl调用进程的进程ID。这意味着这些锁用于不同进程间的上锁,而不是同一进程内不同线程间的上锁。
fcntl记录上锁即可用于读也可用于写,对于文件的任意字节,最多只能存在一种类型的锁(读锁或写锁)。而且,一个给定字节可以有多个读写锁,但只能有一个写入锁。
对于一个打开着某个文件的给定进程来说,当它关闭该文件的任何一个描述字或者终止时,与该文件关联的所有锁都被删除。锁不能通过fork由子进程继承。
NAME fcntl-manipulatefiledescriptor SYNOPSIS #include#include intfcntl(intfd,intcmd,.../*arg*/); DESCRIPTION fcntl()performsoneoftheoperationsdescribedbelowontheopenfiledescriptorfd.Theoperationisdeterminedbycmd. fcntl()cantakeanoptionalthirdargument.Whetherornotthisargumentisrequiredisdeterminedbycmd.Therequiredargumenttype isindicatedinparenthesesaftereachcmdname(inmostcases,therequiredtypeisint,andweidentifytheargumentusingthename arg),orvoidisspecifiediftheargumentisnotrequired. Advisoryrecordlocking Linuximplementstraditional("process-associated")UNIXrecordlocks,asstandardizedbyPOSIX.ForaLinux-specificalternativewith bettersemantics,seethediscussionofopenfiledescriptionlocksbelow. F_SETLK,F_SETLKW,andF_GETLKareusedtoacquire,release,andtestfortheexistenceofrecordlocks(alsoknownasbyte-range,file- segment,orfile-regionlocks).Thethirdargument,lock,isapointertoastructurethathasatleastthefollowingfields(in unspecifiedorder). structflock{ ... shortl_type;/*Typeoflock:F_RDLCK, F_WRLCK,F_UNLCK*/ shortl_whence;/*Howtointerpretl_start: SEEK_SET,SEEK_CUR,SEEK_END*/ off_tl_start;/*Startingoffsetforlock*/ off_tl_len;/*Numberofbytestolock*/ pid_tl_pid;/*PIDofprocessblockingourlock (setbyF_GETLKandF_OFD_GETLK)*/ ... };
其次,文件上锁源自Berkeley的Unix实现支持给整个文件上锁或解锁的文件上锁(filelocking),但没有给文件内的字节范围上锁或解锁的能力。
fcntl模块及基于文件锁的同步功能。
Pythonfcntl模块提供了基于文件描述符的文件和I/O控制功能。它是Unix系统调用fcntl()和ioctl()的接口。因此,我们可以基于文件锁来提供进程同步的功能。
importfcntl classLock(object): def__init__(self,file_name): self.file_name=file_name self.handle=open(file_name,'w') deflock(self): fcntl.flock(self.handle,fcntl.LOCK_EX) defunlock(self): fcntl.flock(self.handle,fcntl.LOCK_UN) def__del__(self): try: self.handle.close() except: pass
应用
我们做一个简单的场景应用:需要从指定的服务器上下载软件版本到/exports/images目录下,因为这个脚本可以在多用户环境执行。我们不希望下载出现冲突,并仅在该目录下保留一份指定的软件版本。下面是基于文件锁的参考实现:
if__name__=="__main__": parser=OptionParser() group=OptionGroup(parser,"FTPdownloadtool","Downloadbuildfromftpserver") group.add_option("--server",type="string",help="FTPserver'sIPaddress") group.add_option("--username",type="string",help="Username") group.add_option("--password",type="string",help="User'spassword") group.add_option("--buildpath",type="string",help="Buildpathintheftpserver") group.add_option("--buildname",type="string",help="Buildnametobedownloaded") parser.add_option_group(group) (options,args)=parser.parse_args() local_dir="/exports/images" lock_file="/var/tmp/flock.txt" flock=Lock(lock_file) flock.lock() ifos.path.isfile(os.path.join(local_dir,options.buildname)): log.info("buildexists,nothingneedstobedone") log.info("Downloadcompleted") flock.unlock() exit(0) log.info("starttodownloadbuild"+options.buildname) t=paramiko.Transport((options.server,22)) t.connect(username=options.username,password=options.password) sftp=paramiko.SFTPClient.from_transport(t) sftp.get(os.path.join(options.buildpath,options.buildname), os.path.join(local_dir,options.buildname)) sftp.close() t.close() log.info("Downloadcompleted") flock.unlock()
更多关于Python相关内容感兴趣的读者可查看本站专题:《Python进程与线程操作技巧总结》、《PythonSocket编程技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》
希望本文所述对大家Python程序设计有所帮助。