Linux内核POSIX文件锁机制深度解析

前言

在操作系统的并发编程世界中,文件锁是确保数据一致性和完整性的关键机制。Linux内核的POSIX文件锁系统是一个精心设计的复杂子系统,它不仅要处理基本的锁获取和释放,还要应对锁冲突、死锁检测、锁合并与分裂等复杂场景。本文将通过深入分析Linux 2.6.10内核中POSIX文件锁的核心实现,揭示其精妙的数据结构设计和高效的算法逻辑。

文件锁机制看似简单,实则蕴含着深刻的设计哲学。从锁冲突检测到死锁预防,从锁合并优化到资源清理,每一个环节都体现了Linux内核开发者对性能、安全性和可靠性的极致追求。通过理解这些底层机制,我们不仅能更好地使用文件锁API,还能从中学习到解决并发问题的通用设计模式

file_lock数据结构关系和作用总结

Linux内核中的文件锁系统通过多个精心设计的链表和指针构建了一个复杂但高效的关系网络,以下是各个关键数据结构的作用和相互关系:

全局文件锁管理系统

file_lock_list(全局文件锁链表)

  • 作用:系统范围内跟踪所有活跃的文件锁
  • 连接方式 :通过file_lock::fl_link连接
  • 用途
    • 系统级锁监控和管理
    • 资源统计和调试信息

blocked_list(全局阻塞锁链表)

  • 作用:跟踪所有被阻塞的锁请求
  • 连接方式 :冲突锁通过file_lock::fl_link连接到此链表
  • 用途
    • 死锁检测的核心数据结构
    • 系统级阻塞状态监控

文件级锁组织

inode::i_flock(文件锁链表)

  • 作用:组织属于同一文件的所有锁
  • 连接方式 :无冲突锁通过file_lock::fl_next连接
  • 用途
    • 锁合并和分裂操作的基础
    • 文件关闭时批量释放所有锁

锁间依赖关系

file_lock::fl_block(阻塞列表)

  • 作用:记录所有等待当前锁的其他锁
  • 连接方式 :等待者通过自身的file_lock::fl_block节点连接到阻塞者的fl_block列表
  • 用途
    • 锁释放时快速找到所有等待者
    • 实现锁的依赖关系管理

file_lock::fl_next(等待关系指针)

  • 作用:记录锁等待关系
  • 取值
    • 无冲突时:指向同一文件链表中的下一个锁
    • 有冲突时:指向正在等待的锁
  • 用途
    • 死锁检测时遍历等待链
    • 维护文件锁链表结构

进程同步机制

file_lock::fl_wait(等待队列头结点)

  • 作用:进程等待锁的队列
  • 用途
    • 锁冲突时进程睡眠等待
    • 锁释放时唤醒等待进程
    • 实现阻塞式锁获取

数据结构关系全景图

复制代码
全局层面:
file_lock_list → 锁A.fl_link → 锁B.fl_link → 锁C.fl_link → ...
blocked_list   → 阻塞锁X.fl_link → 阻塞锁Y.fl_link → ...

文件层面:
inode->i_flock → 锁1.fl_next → 锁2.fl_next → 锁3.fl_next → ...

锁间依赖:
锁X.fl_block → 等待者A.fl_block → 等待者B.fl_block → ...
等待者A.fl_next = 锁X
等待者B.fl_next = 锁X

各数据结构的协同工作流程

  1. 锁申请阶段

    • 遍历inode->i_flock检查冲突
    • 如有冲突,通过fl_next建立等待关系,加入blocked_list
    • 如无冲突,通过fl_next插入文件链表,通过fl_link加入file_lock_list
  2. 锁释放阶段

    • 遍历fl_block列表唤醒所有等待者
    • inode->i_flockfile_lock_list中移除
  3. 死锁检测阶段

    • 通过fl_next指针遍历等待链
    • 检查blocked_list中的循环依赖

POSIX文件锁的核心处理函数__posix_lock_file

c 复制代码
static int __posix_lock_file(struct inode *inode, struct file_lock *request)
{
	struct file_lock *fl;
	struct file_lock *new_fl, *new_fl2;
	struct file_lock *left = NULL;
	struct file_lock *right = NULL;
	struct file_lock **before;
	int error, added = 0;

	/*
	 * We may need two file_lock structures for this operation,
	 * so we get them in advance to avoid races.
	 */
	new_fl = locks_alloc_lock();
	new_fl2 = locks_alloc_lock();

	lock_kernel();
	if (request->fl_type != F_UNLCK) {
		for_each_lock(inode, before) {
			struct file_lock *fl = *before;
			if (!IS_POSIX(fl))
				continue;
			if (!posix_locks_conflict(request, fl))
				continue;
			error = -EAGAIN;
			if (!(request->fl_flags & FL_SLEEP))
				goto out;
			error = -EDEADLK;
			if (posix_locks_deadlock(request, fl))
				goto out;
			error = -EAGAIN;
			locks_insert_block(fl, request);
			goto out;
  		}
  	}

	/* If we're just looking for a conflict, we're done. */
	error = 0;
	if (request->fl_flags & FL_ACCESS)
		goto out;

	error = -ENOLCK; /* "no luck" */
	if (!(new_fl && new_fl2))
		goto out;

	/*
	 * We've allocated the new locks in advance, so there are no
	 * errors possible (and no blocking operations) from here on.
	 * 
	 * Find the first old lock with the same owner as the new lock.
	 */
	
	before = &inode->i_flock;

	/* First skip locks owned by other processes.  */
	while ((fl = *before) && (!IS_POSIX(fl) ||
				  !posix_same_owner(request, fl))) {
		before = &fl->fl_next;
	}

	/* Process locks with this owner.  */
	while ((fl = *before) && posix_same_owner(request, fl)) {
		/* Detect adjacent or overlapping regions (if same lock type)
		 */
		if (request->fl_type == fl->fl_type) {
			if (fl->fl_end < request->fl_start - 1)
				goto next_lock;
			/* If the next lock in the list has entirely bigger
			 * addresses than the new one, insert the lock here.
			 */
			if (fl->fl_start > request->fl_end + 1)
				break;

			/* If we come here, the new and old lock are of the
			 * same type and adjacent or overlapping. Make one
			 * lock yielding from the lower start address of both
			 * locks to the higher end address.
			 */
			if (fl->fl_start > request->fl_start)
				fl->fl_start = request->fl_start;
			else
				request->fl_start = fl->fl_start;
			if (fl->fl_end < request->fl_end)
				fl->fl_end = request->fl_end;
			else
				request->fl_end = fl->fl_end;
			if (added) {
				locks_delete_lock(before);
				continue;
			}
			request = fl;
			added = 1;
		}
		else {
			/* Processing for different lock types is a bit
			 * more complex.
			 */
			if (fl->fl_end < request->fl_start)
				goto next_lock;
			if (fl->fl_start > request->fl_end)
				break;
			if (request->fl_type == F_UNLCK)
				added = 1;
			if (fl->fl_start < request->fl_start)
				left = fl;
			/* If the next lock in the list has a higher end
			 * address than the new one, insert the new one here.
			 */
			if (fl->fl_end > request->fl_end) {
				right = fl;
				break;
			}
			if (fl->fl_start >= request->fl_start) {
				/* The new lock completely replaces an old
				 * one (This may happen several times).
				 */
				if (added) {
					locks_delete_lock(before);
					continue;
				}
				/* Replace the old lock with the new one.
				 * Wake up anybody waiting for the old one,
				 * as the change in lock type might satisfy
				 * their needs.
				 */
				locks_wake_up_blocks(fl);
				fl->fl_start = request->fl_start;
				fl->fl_end = request->fl_end;
				fl->fl_type = request->fl_type;
				fl->fl_u = request->fl_u;
				request = fl;
				added = 1;
			}
		}
		/* Go on to next lock.
		 */
	next_lock:
		before = &fl->fl_next;
	}

	error = 0;
	if (!added) {
		if (request->fl_type == F_UNLCK)
			goto out;
		locks_copy_lock(new_fl, request);
		locks_insert_lock(before, new_fl);
		new_fl = NULL;
	}
	if (right) {
		if (left == right) {
			/* The new lock breaks the old one in two pieces,
			 * so we have to use the second new lock.
			 */
			left = new_fl2;
			new_fl2 = NULL;
			locks_copy_lock(left, right);
			locks_insert_lock(before, left);
		}
		right->fl_start = request->fl_end + 1;
		locks_wake_up_blocks(right);
	}
	if (left) {
		left->fl_end = request->fl_start - 1;
		locks_wake_up_blocks(left);
	}
 out:
	unlock_kernel();
	/*
	 * Free any unused locks.
	 */
	if (new_fl)
		locks_free_lock(new_fl);
	if (new_fl2)
		locks_free_lock(new_fl2);
	return error;
}

函数功能概述

变量声明和锁分配

c 复制代码
struct file_lock *fl;
struct file_lock *new_fl, *new_fl2;
struct file_lock *left = NULL;
struct file_lock *right = NULL;
struct file_lock **before;
int error, added = 0;

/*
 * We may need two file_lock structures for this operation,
 * so we get them in advance to avoid races.
 */
new_fl = locks_alloc_lock();
new_fl2 = locks_alloc_lock();
  • fl: 遍历锁链表时的当前锁
  • new_fl, new_fl2: 预分配的新锁结构,用于锁合并和分裂操作
  • left, right: 当新锁分裂现有锁时,左右部分的锁
  • before: 指向锁链表指针的指针,用于插入操作
  • added: 标记是否已将请求锁合并到现有锁中
  • 锁预分配: 提前分配两个锁结构,避免在持有内核锁时进行内存分配

获取内核锁和冲突检查

c 复制代码
lock_kernel();
if (request->fl_type != F_UNLCK) {
    for_each_lock(inode, before) {
        struct file_lock *fl = *before;
        if (!IS_POSIX(fl))
            continue;
        if (!posix_locks_conflict(request, fl))
            continue;
        error = -EAGAIN;
        if (!(request->fl_flags & FL_SLEEP))
            goto out;
        error = -EDEADLK;
        if (posix_locks_deadlock(request, fl))
            goto out;
        error = -EAGAIN;
        locks_insert_block(fl, request);
        goto out;
    }
}
  • lock_kernel(): 获取大内核锁,保护临界区
  • 跳过解锁请求: 如果是解锁操作(F_UNLCK),不检查冲突
  • 遍历所有锁 : for_each_lock 宏遍历inode的锁链表
  • 过滤POSIX锁: 只处理POSIX风格的锁
  • 冲突检测 : posix_locks_conflict 检查锁类型和范围是否冲突
  • 非阻塞处理: 如果请求不允许睡眠(FL_SLEEP),立即返回-EAGAIN
  • 死锁检测 : posix_locks_deadlock 检查是否会产生死锁
  • 插入阻塞列表: 将请求锁插入到冲突锁的阻塞列表中

访问检查和资源验证

c 复制代码
/* If we're just looking for a conflict, we're done. */
error = 0;
if (request->fl_flags & FL_ACCESS)
    goto out;

error = -ENOLCK; /* "no luck" */
if (!(new_fl && new_fl2))
    goto out;
  • FL_ACCESS标志: 如果只是访问检查(如强制锁定验证),直接返回成功
  • 资源检查: 确保两个预分配的锁结构都成功分配,否则返回-ENOLCK

查找相同所有者的锁

c 复制代码
before = &inode->i_flock;

/* First skip locks owned by other processes.  */
while ((fl = *before) && (!IS_POSIX(fl) ||
              !posix_same_owner(request, fl))) {
    before = &fl->fl_next;
}

/* Process locks with this owner.  */
while ((fl = *before) && posix_same_owner(request, fl)) {
  • 初始化指针 : before 指向锁链表头指针
  • 跳过其他进程的锁: 遍历直到找到相同所有者的POSIX锁
  • 处理同所有者锁: 只处理相同进程拥有的锁,用于锁合并优化

相同类型锁的合并处理

  • 重叠锁的合并(范围有交集)
  • 相邻锁的合并(范围紧挨着)
  • 包含锁的合并(一个锁完全包含另一个)

范围关系判断

c 复制代码
if (fl->fl_end < request->fl_start - 1)
    goto next_lock;
if (fl->fl_start > request->fl_end + 1)
    break;
  • 当前锁在请求锁的左边且不相邻,继续检查下一个锁

  • 当前锁在请求锁的右边且不相邻,终止循环

    • 为什么用 break 而不是 goto next_lock
    • 因为链表是按起始位置排序的
    • 如果当前锁已经在请求锁的右边,后续的锁都在更右边
    • 不需要继续遍历,直接终止循环

锁范围合并

c 复制代码
/* 合并相邻或重叠的锁 */
if (fl->fl_start > request->fl_start)
    fl->fl_start = request->fl_start;
else
    request->fl_start = fl->fl_start;
if (fl->fl_end < request->fl_end)
    fl->fl_end = request->fl_end;
else
    request->fl_end = fl->fl_end;
  • 这个逻辑计算两个锁的最小并集
    • new_start = min(fl_start, request_start);
    • new_end = max(fl_end, request_end);

重复合并处理

c 复制代码
if (added) {
    locks_delete_lock(before);
    continue;
}
request = fl;
added = 1;
  • 处理连续合并的情况,一次请求需要合并多个现有锁
  • added = 1;:这里标记已完成添加,后续不需要单独提供函数locks_insert_lock添加
c 复制代码
// 初始链表: 锁A[0-10] → 锁B[15-25] → 锁C[30-40] → NULL
// 请求锁X: [5-35]

// 处理过程:
// 第一次迭代(锁A):
//   - 合并锁A和锁X: 锁A变为[0-35]
//   - added = 0 → request = 锁A, added = 1

// 第二次迭代(锁B):
//   - 锁B[15-25] 在 锁A[0-35] 范围内
//   - added = 1 → 删除锁B, continue

// 第三次迭代(锁C):
//   - 锁C[30-40] 与 锁A[0-35] 重叠
//   - 合并: 锁A变为[0-40]
//   - added = 1 → 删除锁C, continue

// 最终结果: 锁A[0-40] → NULL

不同类型锁的复杂处理

  • 读锁 vs 写锁的冲突处理
  • 解锁操作的特殊处理
  • 锁分裂场景(一个锁被另一个锁分成几部分)
  • 锁替换场景(新锁完全覆盖旧锁)

范围关系判断

c 复制代码
if (fl->fl_end < request->fl_start)
    goto next_lock;
if (fl->fl_start > request->fl_end)
    break;
  • 当前锁在请求锁的左边,没有重叠,继续检查下一个锁

  • 当前锁在请求锁的右边,没有重叠,终止循环

  • 这里没有使用 +-1,因为不同类型锁即使相邻也不合并

解锁操作处理

c 复制代码
if (request->fl_type == F_UNLCK)
    added = 1;
  • 解锁请求不需要创建新锁
  • 只需要标记 added = 1 表示"已处理"
  • 后续会根据重叠情况删除或修改现有锁

锁分裂记录

c 复制代码
if (fl->fl_start < request->fl_start)
    left = fl;
if (fl->fl_end > request->fl_end) {
    right = fl;
    break;
}
  • 场景1:解锁操作 (F_UNLCK)

    现有锁: [10, 40] (写锁)
    解锁请求: [20, 30] (F_UNLCK)

    分裂后:
    左部分: [10, 19] (写锁)
    右部分: [31, 40] (写锁)
    中间: [20, 30] (已解锁)

  • 场景2:相同所有者的锁类型变更

c 复制代码
现有锁: [10, 40] (读锁) - 进程A
新请求: [20, 30] (写锁) - 进程A(锁升级)

分裂后:
左部分: [10, 19] (读锁) - 进程A
中间: [20, 30] (写锁) - 进程A
右部分: [31, 40] (读锁) - 进程A
  • 因为前面进行了锁合并,所以自然会出现某一部分的锁进行了变更导致的锁分裂

完全覆盖处理

c 复制代码
if (fl->fl_start >= request->fl_start) {
    if (added) {
        locks_delete_lock(before);
        continue;
    }
    locks_wake_up_blocks(fl);
    fl->fl_start = request->fl_start;
    fl->fl_end = request->fl_end;
    fl->fl_type = request->fl_type;
    fl->fl_u = request->fl_u;
    request = fl;
    added = 1;
}
  • 当前锁的开始位置在新锁开始位置的右边或相同,并且前面的判断确保当前锁的结束位置在新锁开始位置的左边或相同

  • 场景1:新锁完全覆盖现有锁

    当前锁: [20, 30] (读锁)
    新请求: [10, 40] (写锁)

    条件: 20 >= 10 → true
    执行: 用新锁完全替换现有锁

重复合并处理

c 复制代码
if (added) {
    locks_delete_lock(before);
    continue;
}
  • 如果已经合并过 (added = 1),删除当前多余的锁
  • 继续处理下一个锁
  • 这里是后面还有其他被新锁完全覆盖的旧锁

锁替换

c 复制代码
locks_wake_up_blocks(fl);
fl->fl_start = request->fl_start;
fl->fl_end = request->fl_end;
fl->fl_type = request->fl_type;
fl->fl_u = request->fl_u;
request = fl;
added = 1;
  1. locks_wake_up_blocks(fl): 唤醒所有等待旧锁的进程
  2. 更新锁范围 :
    • fl->fl_start = request->fl_start
    • fl->fl_end = request->fl_end
  3. 改变锁类型 : fl->fl_type = request->fl_type
  4. 复制用户数据 : fl->fl_u = request->fl_u
  5. 重用锁结构 : request = fl(使用现有锁代替新请求)
  6. 标记已处理 : added = 1
  • 场景1:锁分裂
c 复制代码
// 现有锁: [10, 40] (写锁)
// 新请求: [20, 30] (读锁)

// 处理过程:
// - left = 现有锁 (因为10 < 20)
// - right = 现有锁 (因为40 > 30) → break
// 后续会创建:
//   左锁: [10, 19] (写锁)
//   新锁: [20, 30] (读锁) 
//   右锁: [31, 40] (写锁)
  • 场景2:锁替换
c 复制代码
// 现有锁: [20, 30] (读锁)
// 新请求: [10, 40] (写锁)

// 处理过程:
// - 20 >= 10 → true
// - 唤醒等待现有锁的进程
// - 更新现有锁: [10, 40] (写锁)
// - 重用现有锁结构
  • 场景3:部分重叠
c 复制代码
// 现有锁: [10, 25] (读锁)
// 新请求: [20, 35] (写锁)

// 处理过程:
// - left = 现有锁 (因为10 < 20)
// - 没有right (因为25 < 35)

// 最终结果:
//   左锁: [10, 19] (读锁) ← 由left记录
//   新锁: [20, 35] (写锁) ← 替换后的锁

循环结束和基础检查

c 复制代码
next_lock:
    before = &fl->fl_next;
}

error = 0;
if (!added) {
    if (request->fl_type == F_UNLCK)
        goto out;
    locks_copy_lock(new_fl, request);
    locks_insert_lock(before, new_fl);
    new_fl = NULL;
}
  • 循环结束后的状态
    • before 指针:现在指向链表中合适的位置,新锁应该插入到这里
    • added 标志:表示是否已经将请求合并到现有锁中

新锁插入逻辑

c 复制代码
if (!added) {  // 如果没有合并到现有锁
    if (request->fl_type == F_UNLCK)  // 如果是解锁操作
        goto out;                     // 直接退出(不需要插入新锁)
    
    locks_copy_lock(new_fl, request);    // 复制锁信息到预分配的锁结构
    locks_insert_lock(before, new_fl);   // 将新锁插入到链表中的before位置
    new_fl = NULL;                       // 标记这个锁结构已使用
}

右半部分锁处理

c 复制代码
if (right) {
    if (left == right) {
        left = new_fl2;
        new_fl2 = NULL;
        locks_copy_lock(left, right);
        locks_insert_lock(before, left);
    }
    right->fl_start = request->fl_end + 1;
    locks_wake_up_blocks(right);
}
  • 当新锁在现有锁中间时,需要处理右半部分:

    现有锁: [10, 40] (被分裂)
    新请求: [20, 30]
    分裂后:
    左部分: [10, 19]
    右部分: [31, 40] ← 这就是right

  • 特殊处理:左右相同

c 复制代码
if (left == right) {
    // 这意味着同一个锁既提供了左半部分又提供了右半部分
    // 需要创建右半部分的新锁
    left = new_fl2;                    // 使用第二个预分配锁
    new_fl2 = NULL;                    // 标记已使用
    locks_copy_lock(left, right);      // 复制原锁信息
    locks_insert_lock(before, left);   // 插入左半部分锁
}
  • 调整右半部分范围
c 复制代码
right->fl_start = request->fl_end + 1;
locks_wake_up_blocks(right);
  • 调整范围:右半部分从新锁结束位置+1开始

  • 唤醒等待者:范围改变后唤醒可能正在等待这个锁的进程

  • 左半部分锁处理

c 复制代码
if (left) {
    left->fl_end = request->fl_start - 1;
    locks_wake_up_blocks(left);
}
  • 调整左半部分范围
c 复制代码
left->fl_end = request->fl_start - 1;
locks_wake_up_blocks(left);
  • 调整范围:左半部分结束于新锁开始位置-1
  • 唤醒等待者:范围改变后唤醒等待进程

清理和返回

c 复制代码
out:
unlock_kernel();
if (new_fl)
    locks_free_lock(new_fl);
if (new_fl2)
    locks_free_lock(new_fl2);
return error;
  • 释放内核锁 : unlock_kernel()
  • 释放未使用的锁: 释放预分配但未使用的锁结构
  • 返回错误码: 返回操作结果

函数功能总结

主要功能

__posix_lock_file 是POSIX文件锁的核心处理函数,负责:

  1. 锁冲突检测 - 检查新锁请求与现有锁的冲突
  2. 锁操作处理 - 处理加锁(F_RDLCK/F_WRLCK)和解锁(F_UNLCK)操作
  3. 锁优化合并 - 合并相邻或重叠的同类型锁
  4. 锁分裂处理 - 处理不同类型锁的范围重叠
  5. 死锁预防 - 检测可能的死锁情况

文件锁冲突检测posix_locks_conflict

c 复制代码
static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
	/* POSIX locks owned by the same process do not conflict with
	 * each other.
	 */
	if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl))
		return (0);

	/* Check whether they overlap */
	if (!locks_overlap(caller_fl, sys_fl))
		return 0;

	return (locks_conflict(caller_fl, sys_fl));
}
static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
{
	return ((fl1->fl_end >= fl2->fl_start) &&
		(fl2->fl_end >= fl1->fl_start));
}
static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
	if (sys_fl->fl_type == F_WRLCK)
		return 1;
	if (caller_fl->fl_type == F_WRLCK)
		return 1;
	return 0;
}

posix_locks_conflict 函数

相同所有者检查

c 复制代码
if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl))
    return (0);
  • !IS_POSIX(sys_fl) : 如果系统锁不是POSIX风格的锁,它们不会冲突
    • POSIX锁与其他类型锁(如FLOCK锁)可以共存
  • posix_same_owner(caller_fl, sys_fl) : 如果两个锁属于同一个进程,不冲突
    • 同一个进程可以拥有多个读锁,或者在同一区域有读锁和写锁
  • 返回0: 表示没有冲突

范围重叠检查

c 复制代码
/* Check whether they overlap */
if (!locks_overlap(caller_fl, sys_fl))
    return 0;
  • locks_overlap: 调用辅助函数检查两个锁的范围是否重叠
  • !locks_overlap: 如果锁范围不重叠,肯定没有冲突,返回0

类型冲突检查

c 复制代码
return (locks_conflict(caller_fl, sys_fl));
  • 如果锁属于不同进程且范围重叠,调用 locks_conflict 检查锁类型是否冲突
  • 返回最终的冲突检测结果

locks_overlap 函数

c 复制代码
static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
{
    return ((fl1->fl_end >= fl2->fl_start) &&
        (fl2->fl_end >= fl1->fl_start));
}
  • 条件1 : fl1->fl_end >= fl2->fl_start
    • 锁1的结束位置 >= 锁2的开始位置
    • 锁1的右边界在锁2的左边界的右边
  • 条件2 : fl2->fl_end >= fl1->fl_start
    • 锁2的结束位置 >= 锁1的开始位置
    • 锁2的右边界在锁1的左边界的右边

locks_conflict 函数

c 复制代码
static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
    if (sys_fl->fl_type == F_WRLCK)
        return 1;
    if (caller_fl->fl_type == F_WRLCK)
        return 1;
    return 0;
}

系统锁是写锁

c 复制代码
if (sys_fl->fl_type == F_WRLCK)
    return 1;
  • 如果已存在的系统锁是写锁(F_WRLCK),任何新的锁请求都会冲突
  • 写锁是排他性的,不允许其他任何读锁或写锁

调用者请求写锁

c 复制代码
if (caller_fl->fl_type == F_WRLCK)
    return 1;
  • 如果新请求的锁是写锁(F_WRLCK),与任何现有锁都会冲突
  • 写锁需要独占访问,不允许有其他读锁或写锁

读锁与读锁

c 复制代码
return 0;
  • 只有当两个锁都是**读锁(F_RDLCK)**时才不冲突
  • 多个进程可以同时持有同一区域的读锁

函数功能总结

posix_locks_conflict 功能

主要目的: 综合判断两个POSIX文件锁是否冲突

检测层次:

  1. 锁类型过滤 - 只处理POSIX锁之间的冲突
  2. 所有者检查 - 相同进程的锁不冲突
  3. 范围检查 - 不重叠的锁不冲突
  4. 类型冲突检查 - 检查读/写锁的兼容性

locks_overlap 功能

主要目的: 精确判断两个锁的范围是否重叠

算法原理:

  • 检查两个区间 [start1, end1][start2, end2] 是否有交集
  • 返回布尔值表示是否重叠

locks_conflict 功能

主要目的: 基于锁类型的冲突检测

冲突规则:

  • 写锁 vs 任何锁 = 冲突
  • 任何锁 vs 写锁 = 冲突
  • 读锁 vs 读锁 = 不冲突

死锁检测函数posix_locks_deadlock

c 复制代码
int posix_locks_deadlock(struct file_lock *caller_fl,
				struct file_lock *block_fl)
{
	struct list_head *tmp;

next_task:
	if (posix_same_owner(caller_fl, block_fl))
		return 1;
	list_for_each(tmp, &blocked_list) {
		struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
		if (posix_same_owner(fl, block_fl)) {
			fl = fl->fl_next;
			block_fl = fl;
			goto next_task;
		}
	}
	return 0;
}

函数功能详解

函数声明和变量定义

c 复制代码
int posix_locks_deadlock(struct file_lock *caller_fl,
                struct file_lock *block_fl)
{
    struct list_head *tmp;
  • caller_fl: 当前请求锁的进程持有的锁(请求者)
  • block_fl: 阻塞当前请求的系统锁(被等待的锁)
  • tmp: 用于遍历链表的临时指针

直接死锁检测

c 复制代码
next_task:
    if (posix_same_owner(caller_fl, block_fl))
        return 1;
  • posix_same_owner: 检查两个锁是否属于同一个进程
  • 返回1: 如果属于同一个进程,表示发现死锁

间接死锁检测

c 复制代码
    list_for_each(tmp, &blocked_list) {
        struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
        if (posix_same_owner(fl, block_fl)) {
            fl = fl->fl_next;
            block_fl = fl;
            goto next_task;
        }
    }
  • &blocked_list: 全局的阻塞锁链表,包含所有正在等待锁的请求

  • list_for_each: 遍历链表中的每个元素

  • list_entry: 从链表节点获取包含它的 file_lock 结构

  • fl_link: file_lock 结构中用于连接到 blocked_list 的链表节点

  • 检查当前遍历的锁 fl 是否与阻塞锁 block_fl 属于同一个进程

  • 如果属于同一个进程,说明存在依赖关系链

更新阻塞锁并递归检测

  • fl = fl->fl_next: 获取这个锁正在等待的下一个锁
  • block_fl = fl: 将阻塞锁更新为新的等待目标
  • goto next_task: 跳回开始,用新的阻塞锁重新检测死锁

返回无死锁

c 复制代码
    return 0;
}
  • 如果遍历完整个阻塞列表都没有发现死锁,返回0表示安全

死锁检测算法详解

算法工作原理

  1. 从当前请求开始caller_fl → block_fl
  2. 查找阻塞链block_fl → next_waiting_lock
  3. 检查循环 :如果链条回到 caller_fl 的拥有者,就是死锁

函数功能总结

posix_locks_deadlock 函数用于检测POSIX文件锁请求是否会导致死锁

检测算法

  1. 直接检测:检查请求锁的进程是否正在等待自己持有的锁
  2. 间接检测:通过阻塞链表查找等待依赖链中的循环
  3. 深度优先搜索:沿着等待链递归检测,直到发现循环或链结束

建立锁之间的阻塞等待关系locks_insert_block

c 复制代码
static void locks_insert_block(struct file_lock *blocker, 
			       struct file_lock *waiter)
{
	if (!list_empty(&waiter->fl_block)) {
		printk(KERN_ERR "locks_insert_block: removing duplicated lock "
			"(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
			waiter->fl_start, waiter->fl_end, waiter->fl_type);
		__locks_delete_block(waiter);
	}
	list_add_tail(&waiter->fl_block, &blocker->fl_block);
	waiter->fl_next = blocker;
	if (IS_POSIX(blocker))
		list_add(&waiter->fl_link, &blocked_list);
}
static inline void __locks_delete_block(struct file_lock *waiter)
{
	list_del_init(&waiter->fl_block);
	list_del_init(&waiter->fl_link);
	waiter->fl_next = NULL;
}

locks_insert_block 函数

函数声明和参数

c 复制代码
static void locks_insert_block(struct file_lock *blocker, 
                   struct file_lock *waiter)
  • blocker: 阻塞其他锁的锁(已经持有资源的锁)
  • waiter: 等待资源的锁(被阻塞的锁)

重复插入检查

c 复制代码
if (!list_empty(&waiter->fl_block)) {
    printk(KERN_ERR "locks_insert_block: removing duplicated lock "
        "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
        waiter->fl_start, waiter->fl_end, waiter->fl_type);
    __locks_delete_block(waiter);
}
  • !list_empty(&waiter->fl_block) : 检查等待锁是否已经在某个阻塞列表中
    • fl_block 链表用于连接被同一个锁阻塞的所有等待者
    • 如果不为空,说明这个锁已经存在于某个阻塞关系中
  • 错误打印 : 输出内核错误信息,包括:
    • waiter->fl_pid: 等待锁的进程ID
    • waiter->fl_startwaiter->fl_end: 锁的范围
    • waiter->fl_type: 锁类型(读锁/写锁)
  • __locks_delete_block(waiter): 从之前的阻塞关系中移除这个等待锁

添加到阻塞列表

c 复制代码
list_add_tail(&waiter->fl_block, &blocker->fl_block);
  • &blocker->fl_block: 阻塞锁的阻塞列表头
  • &waiter->fl_block: 等待锁的链表节点
  • list_add_tail: 将等待锁添加到阻塞锁的阻塞列表的尾部
  • 效果 : 现在 waiter 出现在 blocker 的阻塞列表中

设置等待关系

c 复制代码
waiter->fl_next = blocker;
  • waiter->fl_next = blocker: 建立等待关系指针
  • 表示 waiter 正在等待 blocker 释放
  • 这个指针用于死锁检测时遍历等待链

添加到全局阻塞列表

c 复制代码
if (IS_POSIX(blocker))
    list_add(&waiter->fl_link, &blocked_list);
  • IS_POSIX(blocker): 只有当阻塞锁是POSIX类型时才执行
  • &blocked_list: 全局的阻塞锁链表
  • list_add: 将等待锁添加到全局阻塞列表
  • 目的: 便于系统范围内跟踪所有被阻塞的锁请求

__locks_delete_block 函数

c 复制代码
static inline void __locks_delete_block(struct file_lock *waiter)
{
    list_del_init(&waiter->fl_block);
    list_del_init(&waiter->fl_link);
    waiter->fl_next = NULL;
}

从阻塞列表移除

c 复制代码
list_del_init(&waiter->fl_block);
  • list_del_init: 从链表中删除节点并重新初始化
  • &waiter->fl_block: 等待锁在阻塞锁的阻塞列表中的节点
  • 效果: 将等待锁从其当前所在的阻塞列表中移除

从全局列表移除

c 复制代码
list_del_init(&waiter->fl_link);
  • &waiter->fl_link: 等待锁在全局阻塞列表中的节点
  • 效果: 从全局阻塞跟踪列表中移除这个等待锁

清除等待关系

c 复制代码
waiter->fl_next = NULL;
  • waiter->fl_next = NULL: 清除等待关系指针
  • 表示这个锁不再等待任何其他锁
  • 防止悬空指针和错误的死锁检测

函数功能总结

locks_insert_block 功能

主要目的: 建立锁之间的阻塞等待关系

具体操作:

  1. 重复插入检查 - 防止同一个锁被多次加入阻塞关系
  2. 添加到阻塞列表 - 将等待锁添加到阻塞锁的等待者列表中
  3. 设置等待指针 - 建立明确的"谁等待谁"的关系
  4. 全局跟踪 - 将POSIX锁添加到全局阻塞列表用于死锁检测

__locks_delete_block 功能

主要目的: 完全清除锁的阻塞关系

清理操作:

  1. 从阻塞列表移除 - 断开与阻塞锁的关系
  2. 从全局列表移除 - 不再参与系统级死锁检测
  3. 清除等待指针 - 重置等待关系状态

将新的文件锁插入到合适的位置locks_insert_lock

c 复制代码
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
{
	list_add(&fl->fl_link, &file_lock_list);

	/* insert into file's list */
	fl->fl_next = *pos;
	*pos = fl;

	if (fl->fl_ops && fl->fl_ops->fl_insert)
		fl->fl_ops->fl_insert(fl);
}

函数功能详解

c 复制代码
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)

参数说明:

  • pos: 指向文件锁链表中某个位置的指针的指针
  • fl: 要插入的新文件锁结构

关键理解:

  • pos 是一个指向指针的指针,它指向链表中某个节点的 fl_next 字段
  • 这种设计允许函数直接修改链表中的指针

添加到全局文件锁列表

c 复制代码
list_add(&fl->fl_link, &file_lock_list);
  • &file_lock_list: 全局的文件锁链表头,用于跟踪系统中的所有文件锁
  • &fl->fl_link : 新锁的链表节点(list_head 结构)
  • list_add: 内核链表操作函数,将新元素添加到链表头部
  • 效果 : 新锁 fl 被添加到全局文件锁列表的开头

插入到文件的特定锁链表

c 复制代码
/* insert into file's list */
fl->fl_next = *pos;
*pos = fl;

假设我们有这样的文件锁链表:

c 复制代码
// 链表: 锁A → 锁B → 锁C → NULL
// pos 指向 锁A的fl_next 字段(即指向锁B的指针)

第一行:fl->fl_next = *pos;

c 复制代码
// *pos 解引用得到的是 锁B 的地址
// 所以: fl->fl_next = 锁B
// 现在新锁指向原来的下一个锁

第二行:*pos = fl;

c 复制代码
// *pos 是 锁A.fl_next 字段
// 将 锁A.fl_next 设置为新锁 fl
// 现在 锁A 指向新锁

插入后的结果

c 复制代码
// 插入前: 锁A → 锁B → 锁C → NULL
//              ↑
//             pos 指向 锁A.fl_next

// 插入后: 锁A → 新锁fl → 锁B → 锁C → NULL
//                    ↑
//              fl->fl_next = 锁B

调用锁操作的回调函数

c 复制代码
if (fl->fl_ops && fl->fl_ops->fl_insert)
    fl->fl_ops->fl_insert(fl);
  • fl->fl_ops: 锁操作函数表指针,可能为NULL
  • fl->fl_ops->fl_insert: 插入回调函数指针
  • 条件检查: 只有当操作表存在且插入回调函数存在时才调用
  • 回调功能: 允许特定的锁类型在插入时执行自定义操作

可能的回调用途:

c 复制代码
// 例如,网络文件系统可能在锁插入时:
// - 通知远程服务器
// - 更新分布式锁状态
// - 记录审计信息

双重指针的妙用

为什么使用 struct file_lock **pos

这种设计允许函数统一处理三种插入情况:

  1. 链表头插入pos = &inode->i_flock
  2. 中间插入pos = &current_lock->fl_next
  3. 末尾插入pos = &last_lock->fl_next

全局链表 vs 文件链表

两个独立的链表结构

c 复制代码
// 1. 全局文件锁链表 (系统范围)
file_lock_list → 锁X.fl_link → 锁Y.fl_link → 锁Z.fl_link → ...

// 2. 每个文件的锁链表 (按inode组织)
inode->i_flock → 锁A.fl_next → 锁B.fl_next → 锁C.fl_next → ...

全局链表 file_lock_list:

  • 系统管理:查看所有活跃的文件锁
  • 死锁检测:遍历所有锁的等待关系
  • 资源监控:统计锁使用情况

文件链表 inode->i_flock:

  • 冲突检测:检查同一文件的锁冲突
  • 锁合并:优化相同文件的锁范围
  • 文件操作:在文件关闭时释放所有锁

函数功能总结

主要功能

locks_insert_lock 负责将新的文件锁插入到合适的位置

  1. 全局注册 - 将锁添加到系统全局文件锁列表
  2. 局部插入 - 将锁插入到特定文件的锁链表中指定位置
  3. 回调通知 - 调用锁类型的特定插入处理函数

删除一个文件锁locks_delete_lock

c 复制代码
static void locks_delete_lock(struct file_lock **thisfl_p)
{
	struct file_lock *fl = *thisfl_p;

	*thisfl_p = fl->fl_next;
	fl->fl_next = NULL;
	list_del_init(&fl->fl_link);

	fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
	if (fl->fl_fasync != NULL) {
		printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
		fl->fl_fasync = NULL;
	}

	if (fl->fl_ops && fl->fl_ops->fl_remove)
		fl->fl_ops->fl_remove(fl);

	locks_wake_up_blocks(fl);
	locks_free_lock(fl);
}

函数功能详解

c 复制代码
static void locks_delete_lock(struct file_lock **thisfl_p)
  • thisfl_p: 指向文件锁链表中某个位置的指针的指针
  • 这个参数指向链表中要删除的锁的前一个节点的 fl_next 字段

获取锁指针和更新链表

c 复制代码
struct file_lock *fl = *thisfl_p;

*thisfl_p = fl->fl_next;
fl->fl_next = NULL;

第一行:获取要删除的锁

  • *thisfl_p: 解引用得到实际要删除的锁指针
  • fl: 现在指向要删除的锁结构

第二行:从链表中移除

c 复制代码
*thisfl_p = fl->fl_next;
  • 关键操作 : 将前一个节点的 fl_next 指针指向要删除锁的下一个锁
  • 效果: 要删除的锁从链表中被"跳过"

第三行:清除next指针

c 复制代码
fl->fl_next = NULL;
  • 将被删除锁的 fl_next 设置为NULL,表示它不再属于任何链表

链表操作示例:

c 复制代码
// 删除前: lockA → lockB(要删除) → lockC → NULL
// thisfl_p 指向 lockA->fl_next (即指向lockB的指针)

// 执行后:
// *thisfl_p = fl->fl_next;  // lockA->fl_next = lockC
// fl->fl_next = NULL;        // lockB->fl_next = NULL

// 删除后: lockA → lockC → NULL
// lockB 被孤立,准备清理

从全局列表移除

c 复制代码
list_del_init(&fl->fl_link);
  • &fl->fl_link: 要删除锁在全局链表中的节点
  • list_del_init: 内核链表函数,从链表中删除节点并重新初始化
  • 效果 : 锁从全局文件锁列表 file_lock_list 中移除

处理异步通知

c 复制代码
fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
if (fl->fl_fasync != NULL) {
    printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
    fl->fl_fasync = NULL;
}

第一行:清理异步通知

c 复制代码
fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
  • fasync_helper: 内核函数,用于管理文件的异步I/O通知
  • 参数 :
    • 0: fd,删除操作不会使用
    • fl->fl_file: 锁关联的文件
    • 0: 表示删除操作
    • &fl->fl_fasync: 异步通知结构指针的地址
  • 功能: 清理与这个锁相关的异步通知设置

错误检查和清理

c 复制代码
if (fl->fl_fasync != NULL) {
    printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
    fl->fl_fasync = NULL;
}
  • 检查: 如果异步通知结构仍然存在(不应该发生)
  • 错误打印: 输出内核错误信息
  • 强制清理: 将指针设为NULL防止后续问题

调用锁操作的回调函数

c 复制代码
if (fl->fl_ops && fl->fl_ops->fl_remove)
    fl->fl_ops->fl_remove(fl);
  • fl->fl_ops: 锁操作函数表指针
  • fl->fl_ops->fl_remove: 删除回调函数指针
  • 条件调用: 只有当操作表存在且删除回调函数存在时才调用
  • 回调功能: 允许特定的锁类型在删除时执行自定义清理操作

唤醒阻塞的进程

c 复制代码
locks_wake_up_blocks(fl);
  • 遍历这个锁的阻塞列表 (fl->fl_block)
  • 唤醒所有等待这个锁的进程
  • 让被阻塞的锁请求重新尝试获取锁

释放锁内存

c 复制代码
locks_free_lock(fl);
  • 释放 file_lock 结构体占用的内存
  • 将锁资源返还给系统
  • 完成整个删除过程

函数功能总结

主要功能

locks_delete_lock 负责安全彻底地删除一个文件锁,包括所有相关资源的清理

  1. 链表管理 - 从文件和全局链表中安全移除锁
  2. 异步清理 - 处理文件异步通知资源
  3. 回调执行 - 调用锁类型的自定义删除处理
  4. 进程唤醒 - 唤醒所有等待这个锁的阻塞进程
  5. 内存释放 - 最终释放锁结构占用的内存

locks_wake_up_blocks/locks_free_lock

c 复制代码
static void locks_wake_up_blocks(struct file_lock *blocker)
{
	while (!list_empty(&blocker->fl_block)) {
		struct file_lock *waiter = list_entry(blocker->fl_block.next,
				struct file_lock, fl_block);
		__locks_delete_block(waiter);
		if (waiter->fl_lmops && waiter->fl_lmops->fl_notify)
			waiter->fl_lmops->fl_notify(waiter);
		else
			wake_up(&waiter->fl_wait);
	}
}
static inline void __locks_delete_block(struct file_lock *waiter)
{
	list_del_init(&waiter->fl_block);
	list_del_init(&waiter->fl_link);
	waiter->fl_next = NULL;
}
static inline void locks_free_lock(struct file_lock *fl)
{
	if (fl == NULL) {
		BUG();
		return;
	}
	if (waitqueue_active(&fl->fl_wait))
		panic("Attempting to free lock with active wait queue");

	if (!list_empty(&fl->fl_block))
		panic("Attempting to free lock with active block list");

	if (!list_empty(&fl->fl_link))
		panic("Attempting to free lock on active lock list");

	if (fl->fl_ops) {
		if (fl->fl_ops->fl_release_private)
			fl->fl_ops->fl_release_private(fl);
		fl->fl_ops = NULL;
	}

	if (fl->fl_lmops) {
		if (fl->fl_lmops->fl_release_private)
			fl->fl_lmops->fl_release_private(fl);
		fl->fl_lmops = NULL;
	}

	kmem_cache_free(filelock_cache, fl);
}

locks_wake_up_blocks 函数

函数声明

c 复制代码
static void locks_wake_up_blocks(struct file_lock *blocker)
  • blocker: 正在释放的锁,它可能阻塞了其他锁

循环处理所有阻塞的锁

c 复制代码
while (!list_empty(&blocker->fl_block)) {
  • &blocker->fl_block: 阻塞列表头,包含所有等待这个锁的锁
  • !list_empty(): 检查阻塞列表是否非空
  • while 循环: 持续处理直到所有被阻塞的锁都被唤醒

获取第一个等待者

c 复制代码
struct file_lock *waiter = list_entry(blocker->fl_block.next,
        struct file_lock, fl_block);
  • blocker->fl_block.next: 阻塞列表中的第一个节点
  • list_entry: 内核宏,从链表节点获取包含它的结构体
  • struct file_lock: 目标结构体类型
  • fl_block: 在file_lock结构体中的链表字段名
  • 结果 : waiter 指向第一个被阻塞的锁

从阻塞关系中移除

c 复制代码
__locks_delete_block(waiter);
  • 调用辅助函数将等待者从阻塞关系中完全移除
  • 包括从阻塞列表和全局列表中删除

通知或唤醒等待者

c 复制代码
if (waiter->fl_lmops && waiter->fl_lmops->fl_notify)
    waiter->fl_lmops->fl_notify(waiter);
else
    wake_up(&waiter->fl_wait);
  1. 自定义通知(如果有锁管理器操作):

    • waiter->fl_lmops: 锁管理器操作表
    • fl_notify: 自定义通知函数
    • 用于复杂的锁管理器(如分布式锁)
  2. 标准唤醒(默认情况):

    • wake_up(&waiter->fl_wait): 唤醒在等待队列上睡眠的进程
    • waiter->fl_wait: 等待队列头

__locks_delete_block 函数

c 复制代码
static inline void __locks_delete_block(struct file_lock *waiter)
{
    list_del_init(&waiter->fl_block);
    list_del_init(&waiter->fl_link);
    waiter->fl_next = NULL;
}

从阻塞列表移除

c 复制代码
list_del_init(&waiter->fl_block);
  • &waiter->fl_block : 等待者在阻塞者fl_block列表中的节点
  • list_del_init: 从链表中删除并重新初始化节点
  • 效果: 等待者不再属于任何阻塞列表

从全局列表移除

c 复制代码
list_del_init(&waiter->fl_link);
  • &waiter->fl_link: 等待者在全局blocked_list中的节点
  • 效果: 等待者不再参与系统级死锁检测

清除等待关系

c 复制代码
waiter->fl_next = NULL;
  • waiter->fl_next: 指向被等待锁的指针
  • 设置为NULL: 表示这个锁不再等待任何其他锁
  • 防止悬空指针: 确保指针安全

locks_free_lock 函数

函数声明和空指针检查

c 复制代码
static inline void locks_free_lock(struct file_lock *fl)
{
    if (fl == NULL) {
        BUG();
        return;
    }
  • 空指针检查: 如果传入NULL指针,触发BUG(内核错误)
  • 安全返回: 防止后续操作崩溃

活跃状态检查

c 复制代码
if (waitqueue_active(&fl->fl_wait))
    panic("Attempting to free lock with active wait queue");

if (!list_empty(&fl->fl_block))
    panic("Attempting to free lock with active block list");

if (!list_empty(&fl->fl_link))
    panic("Attempting to free lock on active lock list");
  1. 等待队列活跃

    • waitqueue_active(&fl->fl_wait): 检查是否有进程在等待这个锁
    • 如果有,触发panic(严重内核错误)
  2. 阻塞列表非空

    • !list_empty(&fl->fl_block): 检查这个锁是否还阻塞其他锁
    • 如果有,触发panic
  3. 全局链表非空

    • !list_empty(&fl->fl_link): 检查锁是否还在全局链表中
    • 如果有,触发panic

锁操作表清理

c 复制代码
if (fl->fl_ops) {
    if (fl->fl_ops->fl_release_private)
        fl->fl_ops->fl_release_private(fl);
    fl->fl_ops = NULL;
}
  • 检查操作表存在 : if (fl->fl_ops)
  • 调用私有释放 : 如果定义了fl_release_private回调
  • 清空指针 : fl->fl_ops = NULL

锁管理器操作表清理

c 复制代码
if (fl->fl_lmops) {
    if (fl->fl_lmops->fl_release_private)
        fl->fl_lmops->fl_release_private(fl);
    fl->fl_lmops = NULL;
}
  • 针对锁管理器特定的操作表
  • 调用私有资源释放回调
  • 清空指针

内存释放

c 复制代码
kmem_cache_free(filelock_cache, fl);
  • filelock_cache: 文件锁对象的内存缓存(slab分配器)
  • kmem_cache_free: 将对象释放回内存缓存
  • 完成释放: 锁结构占用的内存被回收

函数功能总结

locks_wake_up_blocks 功能

唤醒所有被指定锁阻塞的等待者

  1. 批量处理 - 循环处理所有被阻塞的锁
  2. 关系清理 - 彻底移除阻塞关系
  3. 灵活通知 - 支持标准和自定义唤醒机制

__locks_delete_block 功能

完全清除锁的阻塞关系状态

  1. 列表移除 - 从阻塞列表和全局列表中删除
  2. 指针重置 - 清除等待关系指针
  3. 状态初始化 - 将链表节点重置为初始状态

locks_free_lock 功能

安全释放文件锁内存资源

  1. 状态验证 - 确保锁处于可释放状态
  2. 资源清理 - 调用所有必要的清理回调
  3. 内存回收 - 使用slab分配器高效释放
相关推荐
L.Ru33 分钟前
ubuntu的使用
linux·运维·ubuntu
Clarice__33 分钟前
Ubuntu使用指南——MATLAB篇
linux·ubuntu
lang2015092838 分钟前
什么是 Linux?
linux·运维·服务器
wanderist.44 分钟前
Linux使用经验——mv命令
linux·运维·服务器
誰能久伴不乏1 小时前
进程通信与线程通信:全面总结 + 使用场景 + 优缺点 + 使用方法
linux·服务器·c语言·c++
r***01381 小时前
linux上redis升级
linux·运维·redis
好奇的菜鸟1 小时前
如何在 Ubuntu 系统上完全移除 Docker 及其所有数据
linux·ubuntu·docker
BD_Marathon1 小时前
【Zookeeper】客户端命令行——节点类型
linux·服务器·zookeeper
好奇的菜鸟2 小时前
WSL上的Ubuntu 24.04 (Noble Numbat) 配置阿里云镜像源,提速软件安装
linux·ubuntu·阿里云