soft lockup的线程无法kill的原因

构造简单驱动在内核态死循环场景,第二个write_lock在当前cpu上一直自旋(线程处于R状态),造成内核soft lockup,

238214 root      20   0   13240   1964   1832 R 100.0  0.0  5124099h insmod

[275512.284998] watchdog: BUG: soft lockup - CPU#11 stuck for 22s! [insmod:238214]
[275512.285665] RIP: 0010:queued_write_lock_slowpath+0x4f/0x80
[275512.285675] Call Trace:
[275512.285676]  ? 0xffffffffc0943000
[275512.285678]  _raw_write_lock+0x1e/0x30
[275512.285680]  my_init+0x48/0x70 [test_rwlock]
[275512.285682]  do_one_initcall+0x51/0x1b0
[275512.285684]  ? kmem_cache_alloc_trace+0xa0/0x1c0
[275512.285685]  do_init_module+0x60/0x205
[275512.285688]  load_module+0x21b6/0x2950
[275512.285690]  ? m_show+0x1c0/0x1c0
[275512.285693]  SYSC_finit_module+0xa9/0x100
[275512.285696]  SyS_finit_module+0xe/0x10
[275512.285698]  do_syscall_64+0x6c/0x1b0
[275512.285699]  entry_SYSCALL64_slow_path+0x25/0x25

此时用kill -6,-9均无法将该线程杀死,其中缘由涉及到内核对信号(signal)的处理机制,

先理论分析一下内核线程应不应该被kill -9信号杀死。
假设一个内核线程获取了一个mutex锁后由于某种原因进入了sleep状态(可能是要获取另一个锁,如mutex,因获取不到而睡眠等待),而这个时候向该线程发送一个kill -9信号,系统先把kill信号挂载到该线程的pending list上,然后信号的后续发送流程中会将该线程唤醒,然后该线程在内核态继续运行,然后在他运行过程中被一个中断打断(还没有运行到释放mutex锁的代码之前被打断),然后处理完中断从中断返回内核态(该线程在被中断前在内核态运行,所以中断返回后也是返回内核态)的路径中,内核发现该线程pending list上挂的有未处理的信号,就处理kil -9的信号将该线程杀死。该线程被杀死后就再无机会释放他之前获取到的mutex锁,其他等待该锁的线程就再无机会被唤醒(正常是被该线程调用muetx_unlock释放锁时唤醒)并获取该锁。这样内核就变得紊乱了。

通过实际代码分析,实际也是这样,

对于处于运行状态(TASK_RUNNING)的线程,信号发送后挂到目标线程的信号队列,进程返回用户态的时候在 do_notify_resume() 中处理信号。对一个线程发送一个信号以后,并没有硬中断发生,只是简单把信号挂载到目标线程的信号 pending 队列上去,信号真正得到执行的时机是线程执行完异常/中断返回到用户态的时刻,如下图示:

让信号看起来是一个异步中断的关键就是,正常的用户进程是会频繁的在用户态和内核态之间切换的(这种切换包括:系统调用、缺页异常、系统中断…),所以信号能很快的能得到执行。但这也带来了一点问题,内核进程是不响应信号的,除非它刻意的去查询。所以通常情况下我们无法通过kill命令去杀死一个内核进程。

对于阻塞状态的进程又怎么样来响应信号呢?

让一个进程进入阻塞状态,我们可以选择让其进入可中断(TASK_INTERRUPTIBLE)或者不可中断(TASK_UNINTERRUPTIBLE)状态,比如 mutex 操作分为 mutex_lock() 和 mutex_lock_interruptible()。所谓的可中断和不可中断就是说是否可以被中断信号打断:如果进程处于可中断(TASK_INTERRUPTIBLE)状态,信号发送函数会直接唤醒进程,让进程处理完内核态操作去返回用户态,让进程迅速去执行信号处理函数;如果进程处于不可中断(TASK_UNINTERRUPTIBLE)状态俗称为 D 进程,信号只会挂到信号队列,但是没有机会去立即执行。

kernel/signal.c:

  • __send_signal() -> complete_signal() -> signal_wake_up() -> signal_wake_up_state()

 

参考:

http://kernel.meizu.com/linux-signal.html
https://www.oreilly.com/library/view/understanding-the-linux/0596005652/ch04s09.html 

 

 

 

### Linux Soft Lockup 原因 Soft lockup 是指某些进程或内核线程被卡在特定状态下无法继续执行,尽管系统其他部分仍能正常运作[^1]。这种状况通常是由于以下几个原因之一: - **内核锁争用**:当多个进程试图获取同一个资源上的互斥锁时可能发生死锁情况。 - **无限循环**:如果程序陷入了一个不会退出的循环中,并且该循环不包含任何可打断点,则可能导致处理器持续占用而不响应外部请求。 - **硬件故障**:偶尔也会因为底层硬件层面的问题引起此错误。 具体来说,在Linux环境中遇到 `BUG: soft lockup` 错误提示意味着某颗CPU核心已经停止响应超过一定时间长度(默认为20秒),这期间它既没有处理新的任务也没有完成当前正在做的工作[^2]。 ### 解决方案概述 针对上述提到的各种可能成因,可以采取如下措施来预防和修复soft lockup问题: #### 调试与诊断工具的应用 利用诸如 perf、ftrace 或者 SystemTap 这样的性能分析工具可以帮助定位哪些函数调用路径消耗了大量的CPU周期而未能及时返回控制权给调度器[^3]。 ```bash perf record -g sleep 60; perf report ``` 这段命令会收集一分钟内的采样数据并生成报告用于后续审查。 #### 修改内核参数配置 调整一些关键性的内核参数有助于缓解由竞争条件引发的竞争态异常行为。例如增加最大允许等待的时间间隔(`watchdog_thresh`)可以让系统有更多机会自行恢复而不是立即触发警报;设置更激进的任务迁移策略能够减少跨NUMA节点间通信延迟带来的负面影响等。 ```bash echo "10" > /proc/sys/kernel/watchdog_thresh ``` 通过修改 `/etc/sysctl.conf` 文件中的相应条目实现永久生效。 #### 更新驱动程序版本 对于那些怀疑是由设备驱动缺陷所造成的案例而言,尝试升级到最新稳定版往往可以获得更好的兼容性和稳定性表现。特别是涉及到网络接口控制器(Network Interface Controller, NIC)以及存储子系统的组件时更是如此。 #### 定期维护检查 定期重启服务端机器以清除累积下来的临时文件缓存和其他潜在隐患同样重要。此外还需关注官方安全公告列表里是否有针对性补丁发布可供应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值