本文最后更新于:2025年11月19日 下午
简介
它的API很简单,如下
1 2 3
| #include <sys/file.h>
int flock(int fd, int operation);
|
operation可指定:
- LOCK_SH:设置共享(读)锁
- LOCK_EX:设置独占(写)锁
- LOCK_UN:解锁
它是建议锁。需要所有使用该文件的人都遵循规矩先flock上锁再使用文件才有效。
它是一个系统调用,在内核fs/locks.c中实现。
实现本质
每个打开的文件都对应有fd—file结构—inode结构三种数据结构。fd和file结构是进程自己独有的,inode是文件系统层面,所有进程共享。
flock在inode上记录了加锁的file结构信息,组成一个链表。通过比较file结构,判断是否需要锁定。
所以它的特性很多和文件相关。关键函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| static int flock_lock_inode(struct inode *inode, struct file_lock *request) { struct file_lock *new_fl = NULL; struct file_lock *fl; struct file_lock_context *ctx; int error = 0; bool found = false; LIST_HEAD(dispose);
ctx = locks_get_lock_context(inode, request->fl_type); if (!ctx) { if (request->fl_type != F_UNLCK) return -ENOMEM; return (request->fl_flags & FL_EXISTS) ? -ENOENT : 0; }
if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { new_fl = locks_alloc_lock(); if (!new_fl) return -ENOMEM; }
percpu_down_read_preempt_disable(&file_rwsem); spin_lock(&ctx->flc_lock); if (request->fl_flags & FL_ACCESS) goto find_conflict;
list_for_each_entry(fl, &ctx->flc_flock, fl_list) { if (request->fl_file != fl->fl_file) continue; if (request->fl_type == fl->fl_type) goto out; found = true; locks_delete_lock_ctx(fl, &dispose); break; }
if (request->fl_type == F_UNLCK) { if ((request->fl_flags & FL_EXISTS) && !found) error = -ENOENT; goto out; }
find_conflict: list_for_each_entry(fl, &ctx->flc_flock, fl_list) { if (!flock_locks_conflict(request, fl)) continue; error = -EAGAIN; if (!(request->fl_flags & FL_SLEEP)) goto out; error = FILE_LOCK_DEFERRED; locks_insert_block(fl, request); goto out; } if (request->fl_flags & FL_ACCESS) goto out; locks_copy_lock(new_fl, request); locks_insert_lock_ctx(new_fl, &ctx->flc_flock); new_fl = NULL; error = 0;
out: spin_unlock(&ctx->flc_lock); percpu_up_read_preempt_enable(&file_rwsem); if (new_fl) locks_free_lock(new_fl); locks_dispose_list(&dispose); trace_flock_lock_inode(inode, request, error); return error; }
|
注意事项
从上面的原理可知:
- fork,dup文件描述符后,fd对应的file结构不变,锁定关系不变。
- 单个进程可使用一个fd多次重复加锁(内核实际只加锁一次),所以只需要解锁一次。
- 单个进程对同一文件open两个fd(file结构不同),在这两个fd上依次加锁会死锁
- 关闭文件fd,在该fd上加的锁自动释放
flock命令
有时一个命令或者脚本需要执行很久,有可能多个同时运行,我们需要串行的执行他们,避免出现问题。可以这样
1
| flock /tmp/xxxx.lock your_command
|
比如:
1
| flock /tmp/hexo_post.lock hexo g -d
|