如果一个文件被两个人同时修改会出现什么结果呢?在很多unix系统中,该文件的最后状态取决于写该文件的最后一个进程。但是对于有些应用程序(例如数据库),进程有时需要确保它正在单独写一个文件。为了向进程提供这种功能,商用unix系统提供了记录锁机制。
记录锁的功能是:当一个进程正在读或修改文件的某个部分时,它可以组织其他进程修改同一文件区。对于unix系统而言,“记录”这个次是一种误会,因为unix系统内核根本没有使用文件记录这种概念。更适合的术语可能是“字节范围锁”,因为它锁定的是文件中的一个区域,也可以是整个文件。
在unix早期的时候有一种批评是它们不能用来运行数据库系统,其原因就是这些系统不支持部分的对文件加锁。在unix系统开始进入商用计算机领域时,很多系统开发小组以各种不同方式增加了对记录锁的支持。
早起的伯克利版本只支持flock函数。该函数锁整个文件,不能锁文件中的一部分。
SVR3通过fcntl函数增加了记录锁功能。在此基础上构造了lockf函数,它提供了一个简化的接口。这些函数允许调用者锁一个文件中任意字节数的区域,长至整个文件,短至文件中的一个字节。
POSIX.1标准的基础是fcntl。
所希望的锁类型:共享锁(rdlck),独占性写锁(wrlck),解锁一个区域(unlck)
基本原则是:多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上只能有一个进程独用的一把思索。如果一个给定字节上已经有一把或多把读锁,则不能在该字节上再加死锁,如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁
如果一个进程对一个文件区间已经有了一把锁,后来该进程又企图在同一文件区间再加一把锁,那么新锁将替代老锁。例如,若一进程在某文件的16~32字节区间有一把写锁,然后又试图在16~32字节区间加一把读锁,那么该请求将成功执行(假定其他进程此时并不试图向该文件的同一区间加锁),原来的写锁被替换为读锁。
特别需要注意的是加读锁时,该描述符必须是读打开,加写锁时,该描述符必须是写打开。
在设置或释放文件上的锁时,系统按要求组合或裂开相邻区。例如,若字节100~199是加锁的区,需要解锁第150字节,则内核将维持两把锁,一把用于字节100~149,另一把用于字节151~199.
死锁
如果两个进程相互等待对方持有并且锁定的资源时,则这两个进程就处于死锁状态。如果一个进程已经控制了文件中的一个加锁区域,然后它又试图对另一个今晨控制的区域加锁,则它就会休眠,在这种情况下,有可能会发生死锁。
检测到死锁时,内核必须选择一个进程接受出错返回。在本实例中选择了子进程,这是一个实现细节。在某些系统上,总是紫禁城接到出错信息,在另一些系统上,总是父进程接到出错信息。在某些系统上,当时图使用多把锁,有时是子进程接到出错信息,有时则是父进程接到出错信息。
锁的隐含继承和释放
关于记录锁的自动继承和释放有三条规则:
锁与进程和文件两方面有关。这有两重含义:第一重很明显,当一个进程终止时,它所建立的锁全部释放,第二重意思就不很明显,任何时候关闭一个描述符时,则该进程通过这一描述符可以引用的文件上的任何一把锁都被释放(这些锁都是该进程设置的)。
比如我们在在A文件(文件描述符为10)上加了一把锁,然后B=open(a),接着close(B)这时候A上设置的锁被释放。
由fork产生的子进程不继承父进程所设置的锁。这意味着,若一个进程得到一把锁然后调用fork,那么对于父进程的锁而言,子进程被视为另一个进程,对于父进程处继承过来的任一描述符,子进程需要调用fcntl才能获得它自己的锁,这与锁的作用是相一致的。锁的作用是阻止多个进程同时写同一个文(或同一文件区域),如果子进程继承父进程的锁,则父,子进程就可以同时写同一个文件。
在执行exec后,新成勋可以继承原执行程序的锁。但是注意,如果对一个文件描述符设置了close-on-exec标志,那么当作为exec的一部分关闭该文件描述符时,对相应文件的所有锁都被释放了。
建议性锁和强制性锁
考虑数据库访问程序,如果该库中所有函数都以一致的方法处理记录所,则称使用这些函数访问数据库的任何进程集合为合作进程。如果这些函数是仅有的用来访问数据库函数,那么它们使用建议性锁是可行的。但是建议性锁并不能阻止对数据库文件有写权限的任何其他进程对数据库文件进行随意的写操作。没有使用被认可的方法(数据库函数库)访问数据库的进程是一个非合作进程。
强制性锁使内核对每一个open,read和write系统调用都进行检查,检查调用进程对正在访问的文件是否违背了某一把锁的作用,强制性锁有时也被称为强迫方式锁。