Chinaunix首页 | 论坛 | 博客
  • 博客访问: 343018
  • 博文数量: 56
  • 博客积分: 2058
  • 博客等级: 中尉
  • 技术积分: 688
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-11 09:19
个人简介

code rush

文章分类

全部博文(56)

文章存档

2012年(2)

2011年(54)

分类: LINUX

2011-06-13 00:11:57

今天在论坛看到一个兄弟问的关于flock上锁的一点疑问,自己也不是很了解,特地查找了好多资料终于搞清楚原来 flock上的是建议锁,如果某个进程打开了某文件,并加了建议锁,别的进程打开的时候如果测试是否有锁,根据锁的类型,实现互斥访问,可以查看通过

less /etc/locks 观察一下系统对文件上锁的情况, 多执行几次这个程序。

但是如果打开文件不检查是否有已经上锁,则不能实现文件的互斥访问。

参考:http://www.ibm.com/developerworks/cn/linux/l-cn-filelock/index.html
http://lxr.linux.no/linux+v2.6.39/Documentation/filesystems/mandatory-locking.txt

建议锁:内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。
也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向
文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,
而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为,若是不遵守规则也不会报错。

强制锁,是内核强制的文件锁。每个对文件操作时,例如执行open、read、write等操作时,
OS内部检测该文件是否被加了强制锁,如果加锁导致这些文件操作失败。
也就是内核强制应用程序来遵守游戏规则;



如果一个文件已经被加上了读锁或者共享锁,那么其他进程再对这个文件进行
写操作就会被内核阻止;

如果一个文件已经被加上了写锁或者排他锁,那么其他进程再对这个文件进行
读取或者写操作就会被内核阻止。

如果其他进程试图访问一个已经加有强制锁的文件,进程行为取决于所执行的
操作模式和文件锁的类型。

当前锁类型 阻塞读         阻塞写 非阻塞读  非阻塞写
读锁     正常读取数据 阻塞 正常读取数据           EAGAIN
写锁     阻塞          阻塞 EAGAIN  EAGAIN

unlink() 系统调用并不会受到强制锁的影响,原因在于一个文件可能存在多个硬链接,
此时删除文件时并不会修改文件本身的内容,而是只会改变其父目录中 dentry 的内容。

有些应用中并不适合使用强制锁,所以索引节点结构中的 i_flags 字段中
定义了一个标志位MS_MANDLOCK用于有选择地允许或者不允许对一个文件使用强制锁。
在 super_block 结构中,也可以将 s_flags 这个标志为设置为1或者0,用以表示
整个设备上的文件是否允许使用强制锁。

flock默认在Linux下实现的就是建议锁, 可以通过查看 /etc/locks 观察系统文件加锁
  int flock(int fd, int operation);
其中,参数 fd 表示文件描述符;参数 operation 指定要进行的锁操作,该参数的取值
有如下几种:
LOCK_SH:表示要创建一个共享锁,在任意时间内,一个文件的共享锁可以被多个进程拥有
LOCK_EX:表示创建一个排他锁,在任意时间内,一个文件的排他锁只能被一个进程拥有
LOCK_UN:表示删除该进程创建的锁
LOCK_MAND:它主要是用于共享模式强制锁,它可以与 LOCK_READ 或者 LOCK_WRITE 联合
起来使用,从而表示是否允许并发的读操作或者并发的写操作。

通常情况下,如果加锁请求不能被立即满足,那么系统调用 flock() 会阻塞当前进程。
比如,进程想要请求一个排他锁,但此时,已经由其他进程获取了这个锁,那么该进程
将会被阻塞。如果想要在没有获得这个排他锁的情况下不阻塞该进程,可以将 LOCK_NB 
和 LOCK_SH 或者 LOCK_EX 联合使用,那么系统就不会阻塞该进程。

flock() 所加的锁会对整个文件起作用。

int fcntl(int fields, int cmd, .../* int arg */);
//若成功则依赖于cmd,若出错则返回-1。第三个参数总是一个整数。但是在作为记录锁用时,
第三个参数则是指向一个结构的指针。
fcntl可以改变打开文件的属性。非常强大的一个函数:有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

fcntl文件锁有两种类型:建议性锁和强制性锁。
系统默认fcntl都是建议性锁,强制性锁是非POSIX标准的。如果要使用强制性锁,要使整个
系统可以使用强制性锁,那么得需要重新挂载文件系统, mount使用参数 -0 mand
打开强制性锁,或者关闭已加锁文件的组执行权限并且打开该文件的set-GID权限位。

使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件
解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后
关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。
当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上
的建议性锁(这也是建议性锁和强制性锁的最大区别), 所以不要想设置建议性锁来达到永久
不让别的进程访问文件的目的(强制性锁才可以);强制性锁则对所有进程起作用。所以如果没有
释放强制锁,可能会有麻烦。

fcntl使用三个参数 F_SETLK/F_SETLKW, F_UNLCK和F_GETLK, 来分别要求、释放、测试记录锁,
记录锁是对文件一部分而不是整个文件的锁,这种细致的控制使得进程更好地协作以共享文件资源。

获取,释放或测试记录锁,使用到的参数是以下结构体指针
struct flock {
       ....
             short l_type;    /* 锁类型: F_RDLCK, F_WRLCK, F_UNLCK */
             short l_whence; /* l_start字段参照点: SEEK_SET(文件头), */
/*SEEK_CUR(文件当前位置), SEEK_END(文件尾) */
             off_t l_start;   /* 相对于l_whence字段的偏移量 */
             off_t l_len;     /* 需要锁定的长度 */
             pid_t l_pid;     /* 当前获得文件锁的进程标识(F_GETLK) */
             ...
         };

F_SETLK:在指定的字节范围获取锁(F_RDLCK, F_WRLCK)或者释放锁(F_UNLCK)。如果与另一个
进程的锁操作发生冲突,返回 -1并将errno设置为EACCES或EAGAIN。
F_SETLKW:行为如同F_SETLK,除了不能获取锁时会睡眠等待外。如果在等待的过程中接收到信号,
会立即返回并将errno置为EINTR。
F_GETLK:获取文件锁信息。
F_UNLCK:释放文件锁。

为了设置读锁,文件必须以读的方式打开。为了设置写锁,文件必须以写的方式打开。
为了设置读写锁,文件必须以读写的方式打开。  
 

3.由fork产生的子进程不继承父进程所设置的锁。若一个进程得到一把锁,然后调用fork,
那么对于父进程获得锁而言,子进程被视为另一个进程,对于从父进程处继承过来的任一
描述符,子进程需要调用fcntl才能获得它自己的锁。这与锁的作用是一致的。锁的作用是
阻止多个进程同时操作同一个文件,如果子进程继承父进程的锁,则父子进程操作同一个
文件,这与锁的初衷相违背;

4、在执行exec后,新程序可以继承原执行程序的锁。但是注意,如果对一个文件描述符设置了
close-on-exec标志,那么当作为exec的一部分关闭该文件描述符时,对相应文件的所有锁都被释放。




现在把代码贴上:


  1. #include<stdio.h>
  2. #include<errno.h>
  3. #include<fcntl.h>
  4. #include<stdlib.h>
  5. #include<unistd.h>
  6. #include<sys/file.h>
  7. int main(int argc,char**argv){
  8.         int fd;
  9.         fd=open("test",O_WRONLY);
  10.         if(fd==-1){
  11.                 perror("open");
  12.                 exit(1);
  13.         }
  14.         if(flock(fd,LOCK_EX)!=0){
  15.                 perror("flock");
  16.                 exit(1);
  17.         }
  18.         printf("input any to exit:");
  19.         getchar();
  20.         flock(fd,LOCK_UN);
  21.         close(fd);
  22.         return 0;
  23. }



阅读(3360) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~