首页 > C/C++ > 线程 > Linux多线程编程(八)记录锁
2017
08-18

Linux多线程编程(八)记录锁

八、 记录锁(同步和互斥六)

  1、 基本概念

  2、 API

  3、 flock

 

八、记录锁(同步和互斥六)

1、基本概念

除了POSIX定义的这些同步对象外,*nix系统还提供另外一种锁–记录锁
记录锁用于对文件的访问控制,可以通过文件描述符找到被锁的文件,上锁是通过fcntl来完成的。记录锁可以用于相关联的进程之间也可以用于无关的进程之间。
记录锁的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。记录锁锁定的是文件的一个区域(也可能是整个文件)。记录锁通常维护在内核中,锁的持有者由锁持有者的进程ID标识。

2、API

  1. #include <fcnt1.h>  
  2. int fcntl(int fd ,int cmd,…/* struct flock *arg */ ) ;  
  3. 对于记录锁, cmd是FGETLK、FSETLK或FSETLKW。第三个参数是一个指向flock结构的指针。  
  4. struct flock{  
  5.     short l_type;  
  6.     short l_whence;  
  7.     off_t l_start;  
  8.     off_t l_len;  
  9.     pid_t l_pid;  
  10. };  

flock结构的域极其含义:

  • l_type:所希望的锁类型,F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或F_UNLCK(解锁一个区域)
  • l_start:要加锁或解锁的区域的起始地址,由l_start和l_whence两者决定。l_start是相对位移量(字节),l_whence则决定了相对位移量的起点。
  • l_whence:SEEK_SET,SEEK_CUR,SEEK_END.其含义类似于文件系统中的同名宏的含义
  • l_len: 加锁区域的长度。

关于加锁和解锁区域的还要注意:

  • 该区域可以在当前文件尾端处开始或越过其尾端处开始,但是不能在文件起始位置之前开始或越过该起始位置。
  • 如若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能位置为止。也就是不管添写到该文件中多少数据,它都处于锁的范围。
  • 为了锁整个文件,通常的方法是将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0。

共享读锁(l_type为F_RDLCK)和独占写琐(F_RDLCK)的规则:

  • 多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上的写锁则只能由一个进程独用。更进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁。
  • 加读锁时,该描述符必须是读打开;
  • 加写锁时,该描述符必须是写打开。

fcntl函数的三种命令:

  • F_GETLK:判断由arg所指定的锁是否被另外一把锁所排斥(阻塞)。如果存在一把锁,它阻止创建由arg所描述的锁,则这把现存的锁的信息写到arg指向的结构中。如果不存在这种情况,则除了将l_type设置为F_UNLCK之外,arg所指向结构中的其他信息保持不变。
  • F_SETLK:设置由arg所描述的锁。如果试图设置的锁不被规则所允许,则立即出错返回,此时errno设置为EACCES或EAGAIN。
  • F_SETLKW:它是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。如果由于存在其他锁而导致由arg所要求的锁不能被创建,则调用进程睡眠。如果捕捉到信号则睡眠中断。

一个进程持有的记录锁会在进程关闭与该锁相关联的文件时或者进程终止时被自动删除。记录在进程调用fork时不会被继承。
记录锁不能和标准I/O库一起使用,因为标准I/O库使用了缓存。如果要使用记录锁,就要直接使用read和write。

记录锁也是一种协作性锁(某些系统支持强制性的记录锁。强制性锁机制中,内核对每一个open、read和write都要检查调用进程对正在存取的文件是否违背了某一把锁的作用。)。

3、flock 

Linux提供了另外一个函数,用于对文件进行上锁

int flock(int fd,int operation);其参数及含义

  • LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
  • LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
  • LOCK_UN 解除文件锁定状态。
  • LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。

文件无法同时建立共享锁定和互斥锁定,而当使用dup()或fork()时文件描述词不会继承此种锁定。这个API的语义更清晰,如果指定了LOCK_EX,则只当这个锁没有被解除时,是无法再次上锁的。另外需要注意的是它具有进程生命周期,也就是说一个进程被销毁时,它持有的该锁会被释放,因此可用以实现daemon。

最后编辑:
作者:liujg
真实-不弄虚,不做假,做自己,不违心; 踏实-不浮躁,不盲从,不急功,不近利; 实学-不投机,不取巧,勤于学,精于业。