Chinaunix首页 | 论坛 | 博客
  • 博客访问: 77999
  • 博文数量: 66
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 31
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-14 23:13
文章分类

全部博文(66)

文章存档

2015年(38)

2014年(28)

分类: LINUX

2014-12-23 20:53:42

      linux 文件锁的实现及其使用
1, 文件锁要点
   1.1 文件锁是在一个打开文件的inode->i_flock字段组织成的链表中。
   1.2 同一个进程(或有亲属关系并且继承了打开的文件对象的进程)可以把SH锁换成EX锁,也可以把EX锁换成SH锁,linux会把该进程以前的文件锁删除,并替换成新的锁(把新的锁添加到该文件对象对应的i_node的i_flock为头的链表中)。
   1.3 不同进程间添加文件锁,要遵循一定的规则:同一个文件可以添加多个SH锁,但只能有一个EX锁存在。
   1.4 文件关闭时,该文件对应的文件锁释放,但是其他进程的文件锁还照样存在。
 
2, 数据结构
与文件锁相关数据结构的之间的关系:
task_struct
    |
(struct files_struct *)files
    |
(struct dentry *)f_dentry
    |
(struct inode *)d_inode
    |
(struct file_lock *)i_flock

每个文件可能有多个文件锁,这些文件锁由一个双向链表组织起来,
链表的第一个结点就是inode结构中的i_flock。file_lock结构中的
fl_next执行该双向链表的下一个结点。
//进程描述符结构
struct task_struct {
 ...
 /* filesystem information */
 struct fs_struct *fs;
 /* open file information */
 struct files_struct *files;
 ...
};

/*
 * Open file table structure
 */
// 进程打开一个文件时需要填写的结构
struct files_struct {
        atomic_t count;
        spinlock_t file_lock;     /* Protects all the below members.  Nests inside tsk->alloc_lock */
        int max_fds;
        int max_fdset;
        int next_fd;
        struct file ** fd;      /* current fd array */
        fd_set *close_on_exec;
        fd_set *open_fds;
        fd_set close_on_exec_init;
        fd_set open_fds_init;
        struct file * fd_array[NR_OPEN_DEFAULT];
};
//文件表结构
struct file {
    struct list_head    f_list;
    struct dentry       *f_dentry;
    struct vfsmount         *f_vfsmnt;
    struct file_operations  *f_op;
    ...
}
//目录描述符结构
struct dentry {
    atomic_t d_count;
    unsigned int d_flags;       /* protected by d_lock */
    spinlock_t d_lock;      /* per dentry lock */
    struct inode *d_inode;      /* Where the name belongs to - NULL is * negative */
    ...
};
//文件inode结构
struct inode {
    struct hlist_node   i_hash;
    struct list_head    i_list;
    struct list_head    i_sb_list;
    struct list_head    i_dentry;
    loff_t          i_size;
    ...
    struct file_lock    *i_flock;
    ...
};
//文件锁结构
struct file_lock {
 struct file_lock *fl_next;  //文件锁链表的下一个结点
 struct list_head fl_link;   //活动或阻塞链表的指针
 struct list_head fl_block;  //被文件锁阻塞的等待者
 fl_owner_t fl_owner;
 unsigned int fl_pid;
 wait_queue_head_t fl_wait;  //阻塞进程的等待队列
 struct file *fl_file;
 unsigned char fl_flags;
 unsigned char fl_type;
 loff_t fl_start;
 loff_t fl_end;
 struct fasync_struct * fl_fasync; /* for lease break notifications */
 unsigned long fl_break_time; /* for nonblocking lease breaks */
 struct file_lock_operations *fl_ops; /* Callbacks for filesystems */
 struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */
 union {
  struct nfs_lock_info nfs_fl;
 } fl_u;
};

3, 实现
/**
 * sys_flock: - flock() system call.
 * @fd: the file descriptor to lock.
 * @cmd: the type of lock to apply.
 *
 * Apply a %FL_FLOCK style lock to an open file descriptor.
 * The @cmd can be one of
 *
 * %LOCK_SH -- a shared lock.
 *
 * %LOCK_EX -- an exclusive lock.
 *
 * %LOCK_UN -- remove an existing lock.
 *
 * %LOCK_MAND -- a `mandatory' flock.  This exists to emulate Windows Share Modes.
 *
 * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
 * processes read and write access respectively.
 */
asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
{
 struct file *filp;
 struct file_lock *lock;
 int can_sleep, unlock;
 int error;
 error = -EBADF;
 //检查fd是否是一个有效的文件描述符,若是获取该fd对应的文件对象的指针。
 //否则返回错误。
 filp = fget(fd);
 if (!filp)
  goto out;
 can_sleep = !(cmd & LOCK_NB); //若没有设置LOCK_NB标记,可以睡眠置1
 cmd &= ~LOCK_NB; //清楚cmd中的LOCK_NB标志位
 unlock = (cmd == LOCK_UN); //unlock表示是否删除
 
 //检查文件权限是否可读/写权限,若没有返回一个错误码
 if (!unlock && !(cmd & LOCK_MAND) && !(filp->f_mode & 3))
  goto out_putf;
 //获取一个新的file_lock对象锁,并用适当的锁操作初始化它。
 error = flock_make_lock(filp, &lock, cmd);
 if (error)
  goto out_putf;
 //可以阻塞的话,需要把锁标志位掩码的FL_SLEEP置位
 if (can_sleep)
  lock->fl_flags |= FL_SLEEP;
 error = security_file_lock(filp, cmd);
 if (error)
  goto out_free;
 //若文件操作列表中有flock函数,就调用它,
 //否则调用flock_lock_file_wait函数
 if (filp->f_op && filp->f_op->flock)
  error = filp->f_op->flock(filp,
       (can_sleep) ? F_SETLKW : F_SETLK,
       lock);
 else
  error = flock_lock_file_wait(filp, lock);
 out_free:
 //若新的锁还没有加入到活动或阻塞链表中,则释放它
 if (list_empty(&lock->fl_link)) {
  locks_free_lock(lock);
 }
 out_putf:
 //释放文件结构,删除该打开文件的所有文件锁
 fput(filp);
 out:
 return error;
}
 
/* Fill in a file_lock structure with an appropriate FLOCK lock. */
//该函数获取一个新的file_lock锁对象,并根据cmd初始化它
static int flock_make_lock(struct file *filp, struct file_lock **lock,
  unsigned int cmd)
{
 struct file_lock *fl;
 int type = flock_translate_cmd(cmd);
 if (type < 0)
  return type;
 
 fl = locks_alloc_lock();
 if (fl == NULL)
  return -ENOMEM;
 fl->fl_file = filp; //文件指针
 fl->fl_pid = current->tgid; //获取线程组的pid
 fl->fl_flags = FL_FLOCK; //锁标志位
 fl->fl_type = type;
 fl->fl_end = OFFSET_MAX; //锁整个文件
 
 *lock = fl;
 return 0;
}

/**
 * flock_lock_file_wait - Apply a FLOCK-style lock to a file
 * @filp: The file to apply the lock to
 * @fl: The lock to be applied
 *
 * Add a FLOCK style lock to a file.
 */
int flock_lock_file_wait(struct file *filp, struct file_lock *fl)
{
 int error;
 might_sleep();
 for (;;) {
  error = flock_lock_file(filp, fl);
  if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
   break;
  error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
  if (!error)
   continue;
  locks_delete_block(fl);
  break;
 }
 return error;
}

/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
 * at the head of the list, but that's secret knowledge known only to
 * flock_lock_file and posix_lock_file.
 */
static int flock_lock_file(struct file *filp, struct file_lock *new_fl)
{
 struct file_lock **before;
 struct inode * inode = filp->f_dentry->d_inode;
 int error = 0;
 int found = 0;
 lock_kernel();
 //遍历inode->i_flock指向的链表,并检查该链表上每一个锁的类型。
 for_each_lock(inode, before) {
  struct file_lock *fl = *before;
  if (IS_POSIX(fl)) //是posix锁
   break;
  if (IS_LEASE(fl)) //是租借锁
   continue;
  if (filp != fl->fl_file) //不同文件对象,不同进程/没有文件对象继承关系的进程
   continue;
  if (new_fl->fl_type == fl->fl_type) //文件锁类型相同,什么也不做返回0
   goto out;
  //否则,从索引节点链表和全局文件锁链表中删除该文件锁
  //并呼醒fl_block链表中在该锁的等待队列上睡眠的所有进程,释放file_lock结构
  found = 1;
  locks_delete_lock(before); //锁找到把该锁从inode->i_flock和全局锁链表中删除
  break;
 }
 unlock_kernel();
 //若在执行开锁操作则什么都不做,该锁已经不存在了
 if (new_fl->fl_type == F_UNLCK)
  return 0;
 /*
  * If a higher-priority process was blocked on the old file lock,
  * give it the opportunity to lock the file.
  */
 if (found)
  cond_resched();
 lock_kernel();
 //再次搜索索引节点锁链表,来验证现有的FL_FLOCK锁并不与请求的的锁冲突,
 //在索引节点链表中,肯定没有FL_FLOCK写锁,若正在请求一个写锁,那说明本来就没有写锁
 for_each_lock(inode, before) {
  struct file_lock *fl = *before;
  if (IS_POSIX(fl))
   break;
  if (IS_LEASE(fl))
   continue;
  if (!flock_locks_conflict(new_fl, fl)) //新锁和链表中的锁都不是EX(WRLCK)锁
   continue;
  error = -EAGAIN;
  //若发现冲突:若fl_flags的FL_SLEEP标志位被设置,这把新的锁,插入到blocker锁循环链表和全局阻塞链表中。
  if (new_fl->fl_flags & FL_SLEEP) {
   locks_insert_block(fl, new_fl);
  }
  goto out;
 }
 //若不存在冲突,把新的file_lock结构插入到索引节点锁链表和全局文件锁链表中,返回0
 locks_insert_lock(&inode->i_flock, new_fl);
 error = 0;
out:
 unlock_kernel();
 return error;
}

4, 使用实例
 
//共享文件对象的进程间,可以替换掉以前的文件锁。
int main(void)
{
    int fd;
    pid_t pid;
    fd = open("all.h", O_RDWR);
    if (-1 == fd) {
        perror("open");
        goto done;
    }  
    pid = fork();  
    if (pid == 0) { //child
        sleep(2);          
        if(-1 == flock(fd, LOCK_EX|LOCK_NB)) {
            perror("flock");
            goto done;
        }  
        fprintf(stderr, "ex lock ok!\n");
        while (1);
    } else if ( pid > 0) {
        // parents
        if(-1 == flock(fd, LOCK_SH)) {
            perror("flock");           
            goto done;
        }  
        fprintf(stderr, "sh lock ok!\n");
        while (1);
    } 
}
 
执行结果:
[root@localhost stdkernel]# ./dolock
sh lock ok!
ex lock ok!
 
根据源码的解析,kernel用ex锁替换掉了sh锁,现在打开的文件只有一把ex锁,sh已被删掉。
 
 
参考资料:
 kernel-2.6.11源码
 <>
 
阅读(418) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~