FD vs OFD
- FD (File Descriptor) :在进程内打开一个文件,返回一个FD,用户态的概念。
-
OFD(Open File Descriptor) :第一次打开文件时候,在内核创建一个OFD。如果不同进程打开同一个文件,不同的fd会对应同一个内核态的open fd。
image.png
- 调用
dup()
,dup2()
, orfcntl()
会导致同一个进程的不同fd指向同一个内核态中的open fd。 - 调用
folk()
会导致不同进程的fd,对应同一个内核态open fd。 - 如果不是上述两种情况,进程1和进程2打开同一个文件,会在内核态中有两个open fd。
flock API (语意来自BSD,早期Linux不支持)
flock提供了文件粒度的的lock,以下是操作方式:
- LOCK_SH : shared lock
- LOCK_EX : exclusive lock
- LOCK_UN : unlock
- 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提供了字节范围粒度的lock,又称为
record lock
。 - 使用fcntl需要提供fd,cmd和flock结构体。
- fcntl的owner是进程,不是fd,也不是inode
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
- F_SETLK : accquire nonblock lock ,如果被lock立刻返回EACCES or EAGAIN
- F_SETLKW : accquire blocking lock
- 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;
//...
}
参考
- The Linux Programming Interface中的Chapter 4和Chapter 55
- Advanced Programming in the UNIX® Environment, Third Edition中的Chapter 14.3
网友评论