0
0

Linux的文件锁机制初探

Felix021 发表于 2016年04月24日 17:10 | Hits: 182
Tag: 操作系统

# Linux下的文件锁

Linux下有两种不同的文件锁机制,一种是通过 `flock` 系统调用锁定整个文件,另一种是通过 `fcntl` 系统调用完成更细粒度的记录锁(锁定文件中的一个区间)

## open 系统调用

Linux下,在最初打开一个文件的时候,最终都是落到 `open` 这个系统调用上。

open系统调用 (@./fs/open.c) 里头做了很多事情,跟文件锁有关的主要是分配了一个 `struct file` 对象,并且给该对象的 `struct inode *inode` 和 `struct file_operations *f_op` 成员赋值。

其中 inode 是对应文件系统分配的(比如 ext4 文件系统的分配函数是 fs/ext4/inode.c 中的 `ext4_iget`),其中也包含了一个 `struct file_operations *` 类型的成员 `i_fop`,即针对该文件系统文件操作的函数指针集合。

以上提到的两次 `struct file_operations` 对象包含 `flock` 和 `lock` 两个函数指针,分别用于上述的两种不同锁机制。

内核在内存中只会给一个文件分配一个 `struct inode` 对象,不同进程打开同一个文件时,都会单独分配一个 `struct file` 对象,且 `file->inode` 指向该文件的 inode 对象。其中 `file->f_op` 实际上就是在 open 系统调用过程中,从 `inode->i_fop` 拷贝过来的。(open -> do_sys_open -> do_filp_open -> path_openat -> do_last -> vfs_open -> do_dentry_open: `f->f_op = fops_get(inode->i_fop)`)

## flock 加锁

内核首先根据第一个参数 fd 找到内核中的 `struct file` 对象,判断 `file->f_op->flock` 指针是否有效,如果有效(说明是文件系统指定了加锁机制,例如 fs/fuse/file.c 中指定了 `fuse_file_flock` 函数,而ext2/3/4都没有指定),就调用该指针指向的函数来执行加锁操作;如果不存在,则执行`flock_lock_file_wait` -> `flock_lock_inode` (@ fs/locks.c)。

如果文件系统没有指定加锁机制,那么 `flock_lock_inode` 会尝试给该文件对应的 inode 加文件锁(加锁机制比较琐碎,我没看得太细,大致是获取 `inode->i_flctx->flc_lock` 这个spinlock,然后增删改查 `inode->i_flctx->flc_flock` 这个链表,但是可以看出全是内存操作)。

如果文件系统指定了加锁机制,那么就要具体考察对应的加锁机制是否是持久化在磁盘上了;不过从加锁这个需求本身去考察,大多数情况下应当是期望能够快速完成,除了特殊情况,可以认为只是在内存中操作,并没有持久化到磁盘。

## fcntl 加锁

与 `flock` 类似,区别在于判断的函数指针是 `file->f_op->lock` ,如果不存在的话,则调用 `posix_lock_file` 尝试对该 inode 加记录锁,也是在 `inode->i_flctx` 上面倒腾,不过用的是 `inode->i_flctx->flc_lock` 这个链表,具体操作更复杂一些,还要考虑记录锁区间的交叉等问题。

## 结论

具体情况取决于文件系统是否使用了指定的加锁机制,如果有,要单独分析。

如果没有,所有的锁都是在内存中维护的,如果断电,锁自动失效。这样也比较符合通常的理解。

另外,针对文件的加锁行为一般都是进程执行的,进程退出(包括意外退出)时,exit 系统调用会调用 `exit_files` 函数清理该进程打开的所有文件。

原文链接: https://www.felix021.com/blog/read.php?2160

0     0

评价列表(0)