SQLite 学习之路 第九节 锁
文件加锁方法主要有POSIX Locking、No-op Locking、dotfile-file Locking、flock Locking、Named Semaphore Locking以及 AFP Locking6种。介绍如下代码下文分析:
POSIX Locking: POSIX locking(POSIX咨询锁)主要依靠设计。ANSI标准1003.1(1996)部分6.5.2.2 的483到490行规定,当一个进程设置或者清除一个锁的时候,这个操作将会覆盖任何之前由相同进程设置的锁。它并没有明确地说明,但是这意味着它将覆盖通过同一进程使用一个不同的文件描述符设置的锁。例如: int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); 假设./file1和./file2实际上是同一个文件(因为一个是硬链接或符号链接到另一个),然后如果你在fd1设置排它锁,然后试图在fd2获得排它锁。根据它的工作原理,可以预期第二锁定失败,因为由于fd1文件已经锁定。但结果却不是这样的。因为锁来自相同的进程,第二个覆盖了第一个,尽管他们在不同的文件名打开的不同的文件描述符上。 出现这种情况意味着我们不能使用 POSIX 锁来同步竞争同一进程的线程之间的文件访问。POSIX锁可以同步不同进程的线程的访问,但不能同步同一进程的线程访问。若想解决这一问题,SQLite必须在其内部管理文件锁定。每当打开一个新的数据库,我们必须找到特定的数据库文件的i节点(这个i节点由st_dev和st_ino字段的统计结构的fstat()函数填写),并且检查这个锁已经存在在这个i节点上了。当创建或删除锁,我们要看看我们自己内部的锁记录,看是否有另一个线程先在相同的i节点上设置了锁。 POSIX的sqlite3_file结构不再仅仅是一个整型的文件描述符。它现在是一个包含整型的文件描述符和指向描述内部相应的i节点上的锁的结构的指针。每个i节点有一个锁结构,所以如果同一个inode打开两次,两个unixFile结构指向同一个锁结构。锁定结构保持引用计数(因此,我们将知道什么时候删除它)并且“cnt”字段告诉我们其内部锁状态。 cnt==0意味着文件没有加锁。cnt=-1意味着文件上有一个排它锁。cnt>0意味着该文件上有cnt共享锁。任何企图要锁定或解锁文件首先要检查锁结构。如果内部锁结构在锁定和解除锁定的状态之间转换,才会调用 fcntl() 系统调用设置 POSIX 锁。 但POSIX 咨询锁还是存在一些问题。如果你关闭一个指向已锁定的文件的文件描述符,由当前进程对该文件的设置的所有锁都会释放。为了解决这个问题,每个 unixInodeInfo 对象维护在该i节点上挂起的锁的数目的计数。当试图关闭一个unixFile,如果在在该持有锁的i节点上有其他unixFile打开,close()关闭文件描述符的调用将推迟,直到所有的锁都清除。unixInodeInfo结构体保存一个需要关闭的文件描述符列表,并且最后一个锁清除时该列表也将移除(清除)。 另外一个问题是LinuxThreads不能与posix locks很好地工作。许多旧版本的linux使用LinuxThreads库是不兼容posix的。在LinuxThreads下,线程A创建的锁不能由线程B修改或者重写。只有线程A可以修改这个锁。如果应用程序在Linux上使用新的本机Posix线程库(NPTL),锁定行为是正确的,使用NPTL线程A创建的锁可以重写线程B中的锁。但是没有办法知道在编译时使用的是哪一种线程库。 所以没有办法知道在编译时线程A是否可以重写线程B上的锁。必需在运行时检查发现当前进程的行为。
No-op Locking(无操作锁)相对其他一系列锁的实现来说,是迄今为止最简单的,因为它没有试图锁住数据库文件进行读或者写。这种模式的锁适用于只读的数据库,例如刻在CD-ROM上的数据库,当然,当一个应用程序使用外部机制去阻止两个或两个以上的数据库连接对同一个数据库同时进行访问时也可以使用无操作锁。但是,当这种锁用于大量的数据库连接同时访问一个数据库,并且这些连接当中有一个或者更多的是进行写操作的情况的时候,就存在严重的数据库损坏风险。
dot-file Locking(点文件锁)的实现是使用存在单独的锁文件,就是一种目录来控制对数据库的访问。它的工作原理是在同一个目录里面创建有相同的扩展名“.lock”的次目录并存入数据库。一个锁目录的存在就意味着它是排他锁,其它所有类型的锁,包括共享锁、保留锁、未决锁,都会映射到排他锁。这种机制可以用于任意一种文件系统,同时它也存在严重的缺陷: (1)它没有并发性,一个读者就阻碍了其他所有要对数据库进行读写的连接; (2)应用程序的崩溃或功率的耗损都会使旧的锁文件无法自动清理,而需要手动清除不过,点文件锁是一种适用于没有其他锁策略可用的情况的锁机制。
flock Locking(聚集锁)在SQLite支持各种粒度锁级别紧缩成一个单独的排他锁,这一点上和点文件锁是一样的。换句话说,共享锁,保留锁和未决锁对于排他锁来说是一样的。当你使用聚集锁时,SQLite仍然会正常工作,但是并发性降低,因为同一时间只有一个进程可以读取数据库。
Named Semaphore Locking(命名信号锁)其实就像点文件锁和聚集锁一样,实际上只支持排他锁。只有单个进程可以在同一时间对数据库文件进行读或写。这虽然减少了数据库潜在的并发性,却使得这种锁的实现更加容易。但是,只有VxWorks操作系统支持命名信号锁。
AFP(Apple Filing Protocol)是苹果文件协议,它是一个专有的网络协议,为Mac OS X和原始Mac OS提供文件服务。除了AFP,MacOSX也支持服务器消息块(SMB)、网络文件系统(NFS),文件传输协议(FTP)等几个文件服务。AFP支持先进的文件锁定,并且它是建立在苹果麦金塔什电脑(包括OS9 和OSX操作系统)之上的网络文件系统。AFP的第三方实现是有效的,但是SQLite中AFP锁是MacOSX操作系统特有的,不适合其它的unix平台。在代码实现过程中,如果我们不用mac去编译它,那么unix-afp的虚拟文件系统就不能用。另外,NFS锁也是MacOSX特有的锁模式。
|