Linux Lock - flock and fcntl

2019-08-20  本文已影响0人  帆子_8c3a

FD vs OFD

  1. 调用dup(),dup2(), or fcntl()会导致同一个进程的不同fd指向同一个内核态中的open fd。
  2. 调用folk()会导致不同进程的fd,对应同一个内核态open fd。
  3. 如果不是上述两种情况,进程1和进程2打开同一个文件,会在内核态中有两个open fd。

flock API (语意来自BSD,早期Linux不支持)

flock提供了文件粒度的的lock,以下是操作方式:

  1. LOCK_SH : shared lock
  2. LOCK_EX : exclusive lock
  3. LOCK_UN : unlock
  4. LOCK_NB : nonblocking lock,可以用|操作符和前两个联合使用

需要注意的:
flock的owner是内核态open fd,而不是用户态fd,也不是inode。所以dup(),dup2(), or fcntl(),or folk()调用后,虽然返回不同的fd,但本质还是同一个内核open fd。假设进程1的fd3和进程2的fd4指向同一个内核open fd,对fd3加EX锁后,fd4依然可以获得EX锁。
例子1:
下面的fd和newfd对应同一个内核open fd,所以代表同一个owner。

flock(fd, LOCK_EX);               /* Gain lock via 'fd' */
newfd = dup(fd);                  /* 'newfd' refers to same lock as 'fd' */
flock(newfd, LOCK_UN);            /* Frees lock acquired via 'fd' */

例子2:
虽然打开同一个文件,但对应两个不同内核open fd,所以fd1和fd2对应不同的owner。

fd1 = open("a.txt", O_RDWR);
fd2 = open("a.txt", O_RDWR);
flock(fd1, LOCK_EX);
flock(fd2, LOCK_EX);             /* Locked out by lock on 'fd1' */

例子3:
子进程把父进程获得的lock关掉了。

flock(fd, LOCK_EX);              /* Parent obtains lock */
if (fork() == 0)                 /* If child... */    
    flock(fd, LOCK_UN);          /* Release lock shared with parent */

flock command

linux提供了这个命令,本质是调用了flock API。
一般是通过它创建排他锁,来保证某进程是系统内唯一的。

flock -xn /tmp/test.lock -c '/bin/sh /tmp/test.sh'

fcntl API (POSIX语意的lock)

fcntl(fd, cmd, &flock); 

The flock structure

struct flock {    
    short l_type;      /* Lock type: F_RDLCK, F_WRLCK, F_UNLCK */    
    short l_whence;    /* How to interpret 'l_start': SEEK_SET, SEEK_CUR, SEEK_END */    
    off_t l_start;     /* Offset where the lock begins */    
    off_t l_len;       /* Number of bytes to lock; 0 means "until EOF" */    
    pid_t l_pid;       /* Process preventing our lock (F_GETLK only) */
};

cmd

  1. F_SETLK : accquire nonblock lock ,如果被lock立刻返回EACCES or EAGAIN
  2. F_SETLKW : accquire blocking lock
  3. F_GETLK : check lock

内核中的实现

flock和fcntl在内核中都用struct file_lock实现。其主要差别就在于owner的不同。如果lock的owner相同,conflict的检测就会跳过,即相同owner的lock可以递归申请。

flock在内核中的实现

flock转换成struct file_lock

static struct file_lock *
flock_make_lock(struct file *filp, unsigned int cmd)
{
    struct file_lock *fl;
      //...
    fl->fl_file = filp;
    fl->fl_owner = filp; 
    fl->fl_flags = FL_FLOCK;
      //... 
    return fl;
}

判断lock是否来自同一个owner

static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
    /* FLOCK locks referring to the same filp do not conflict with
     * each other.
     */
//对于flock来说,内核态中的open fd相同owner即相同
    if (caller_fl->fl_file == sys_fl->fl_file)
        return (0);
//...
}

fcntl在内核中的实现

fcntl转换成struct file_lock

static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
                 struct flock64 *l)
{
      //...
//对于posix lock来说,进程相同,owner即相同
    fl->fl_owner = current->files; //
    fl->fl_file = filp;
    fl->fl_flags = FL_POSIX;
      //...
    return assign_type(fl, l->l_type);
}

判断lock是否来自同一个owner

static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
{
//...
    return fl1->fl_owner == fl2->fl_owner;
}

Advisory Lock vs Mandatory Lock

Linux对Mandatory Lock支持不好,很多文件系统不支持。例如

int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
{
//...
    /*
     * The NFSv4 protocol doesn't support LOCK_MAND, which is not part of
     * any standard. In principle we might be able to support LOCK_MAND
     * on NFSv2/3 since NLMv3/4 support DOS share modes, but for now the
     * NFS code is not set up for it.
     */
    if (fl->fl_type & LOCK_MAND)
        return -EINVAL;
//...
}

参考

  1. The Linux Programming Interface中的Chapter 4和Chapter 55
  2. Advanced Programming in the UNIX® Environment, Third Edition中的Chapter 14.3
上一篇 下一篇

猜你喜欢

热点阅读