lockdep_assert_held 简介

lockdep_assert_held() 是 Linux 内核中的一个断言宏,用于验证在代码执行到特定点时,指定的锁已经被持有。这是一种锁定点注释机制,属于 Linux 内核死锁检测系统 Lockdep 的一部分[5][6]。

工作原理

lockdep_assert_held() 宏的主要功能是在运行时验证锁的状态,确保在执行关键代码段时,必要的锁已经被获取。这有助于防止由于锁使用不当而导致的并发问题,例如竞态条件和死锁。

在 Linux 内核源码中,我们可以看到它的实际应用场景:

void resched_curr(struct rq *rq)
{
    struct task_struct *curr = rq->curr;
    int cpu;
    
    lockdep_assert_held(&rq->lock);
    
    /* 检查当前进程是否已经设置了调度标志,如果是,则不用再设置一遍,直接返回 */
    if (test_tsk_need_resched(curr))
        return;
    
    /* 根据rq获取CPU */
    cpu = cpu_of(rq);
    /* 如果CPU = 当前CPU,则设置当前进程需要调度标志 */
    if (cpu == smp_processor_id()) {
        /* 设置当前进程需要被调度出去的标志,这个标志保存在进程的thread_info结构上 */
        set_tsk_need_resched(curr);
        /* 设置CPU的内核抢占 */
        set_preempt_need_resched();
        return;
    }
    
    /* 如果不是处于当前CPU上,则设置当前进程需要调度,并通知其他CPU */
    if (set_nr_and_not_polling(curr))
        smp_send_reschedule(cpu);
    else
        trace_sched_wake_idle_without_ipi(cpu);
}

在这个例子中,lockdep_assert_held(&rq->lock) 确保在执行 resched_curr() 函数时,运行队列的锁 rq->lock 已经被持有[1]。

使用场景

lockdep_assert_held() 主要在以下场景中使用:

  1. 关键代码段保护:确保在访问共享资源时已经获取了适当的锁。

  2. 调度器代码:如上例所示,在进程调度相关的代码中,确保在修改运行队列状态时持有相应的锁[1][7]。

  3. 驱动程序开发:在设备驱动程序中保护共享资源访问[5]。

  4. 防止死锁:作为 Lockdep 系统的一部分,帮助开发者识别潜在的死锁问题[6]。

与其他锁断言的区别

Linux 内核中还有其他类似的锁断言宏,如:

  • lockdep_assert_not_held():断言指定的锁当前未被持有
  • lockdep_is_held():检查锁是否被持有,但不会断言失败

这些断言宏在开发和调试阶段非常有用,可以帮助开发者验证锁的使用是否符合预期,从而减少并发相关的错误。

在实际应用中,lockdep_assert_held() 是一种防御性编程技术,有助于确保代码的并发安全性,特别是在复杂的内核子系统中,如进程调度器、内存管理器等。

Citations:
[1] https://www.cnblogs.com/tolimit/p/4335681.html
[2] https://www.cnblogs.com/binlovetech/p/17373667.html
[3] https://rebootcat.com/2020/09/26/epoll_cookbook/
[4] https://github.com/gatieme/LDD-LinuxDeviceDrivers/blob/master/study/kernel/01-process/05-schedule/09-tuning/02-sched_feature/README.md
[5] https://github.com/apachecn/apachecn-linux-zh/blob/master/docs/linux-kernel-prog/13.md
[6] https://blog.csdn.net/weixin_43780213/article/details/133639729
[7] https://s3.shizhz.me/linux-sched/cfs-sched/logic-preempt
[8] https://blog.csdn.net/qq_41104683/article/details/132128593

linux内核函数rate = clk_round_rate(d->clk, baud * 16)是如何实现动态调整时钟的,源码如下: long clk_round_rate(struct clk *clk, unsigned long rate) { struct clk_rate_request req; int ret; if (!clk) return 0; clk_prepare_lock(); clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate); req.rate = rate; ret = clk_core_round_rate_nolock(clk->core, &req); clk_prepare_unlock(); if (ret) return ret; return req.rate; } EXPORT_SYMBOL_GPL(clk_round_rate); static void clk_core_get_boundaries(struct clk_core *core, unsigned long *min_rate, unsigned long *max_rate) { struct clk *clk_user; *min_rate = core->min_rate; *max_rate = core->max_rate; hlist_for_each_entry(clk_user, &core->clks, clks_node) *min_rate = max(*min_rate, clk_user->min_rate); hlist_for_each_entry(clk_user, &core->clks, clks_node) *max_rate = min(*max_rate, clk_user->max_rate); } tatic int clk_core_round_rate_nolock(struct clk_core *core, struct clk_rate_request *req) { struct clk_core *parent; long rate; lockdep_assert_held(&prepare_lock); if (!core) return 0; parent = core->parent; if (parent) { req->best_parent_hw = parent->hw; req->best_parent_rate = parent->rate; } else { req->best_parent_hw = NULL; req->best_parent_rate = 0; } if (core->ops->determine_rate) { return core->ops->determine_rate(core->hw, req); } else if (core->ops->round_rate) { rate = core->ops->round_rate(core->hw, req->rate, &req->best_parent_rate); if (rate < 0) return rate; req->rate = rate; } else if (core->flags & CLK_SET_RATE_PARENT) { return clk_core_round_rate_nolock(parent, req); } else { req->rate = core->rate; } return 0; }
03-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值