news 2026/7/31 9:25:35

Linux内核锁机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核锁机制

Linux内核为了解决并发访问共享资源时带来的数据不一致的问题,提供了锁机制。下面可以看一下实际的例子

共享访问问题

假设有两个中断处理程序(或者两个CPU核心)同时修改一个全局变量count

int count = 10; void isr1(void) { count++; } void isr2(void) { count++; }

在C语言中count++看起来是一行代码,实际在CPU层面,被翻译成3条指令

1、从内存取出count的值 (读)

2、将count的值加1(改)

3、将count的新值重新写会内存 (写)

当两个CPU同时访问时,会发生以下事情

时刻 CPU0(中断0) CPU1(中断1) T1 LOAD count (10) T2 LOAD count (10) T3 ADD (10 → 11) T4 ADD (10 → 11) ← 基于旧值! T5 STORE count (11) T6 STORE count (11) ← 覆盖! 结果:执行了两次 count++,期望 12,实际得到 11 ❌

以上的情况就是经典的访问共享数据产生的问题。如果没有锁,访问共享数据会导致数据错乱。

为了解决此类,Linux内核实现了互斥锁和自旋锁两种机制。实际上锁和解锁的实现依赖于CPU的硬件架构,不同架构的CPU在实现锁的机制上都会不同,主要是通过操作CPU硬件实现锁功能。而内核则是在这些硬件上重新进行封装,以匹配不同的CPU。

自旋锁(spinlock)

自旋锁是一种忙等待的锁机制。当一个线程尝试获取已被其他线程持有的自旋锁时,它不会进入睡眠状态,而是在循环中不断检查锁的状态(就像在原地“自旋”),直到锁被释放。

自旋锁初始化

自旋锁初始化有两种初始化,分别是静态初始化和动态初始化

// 直接定义并初始化一个名为 my_lock 的自旋锁 DEFINE_SPINLOCK(my_lock); spinlock_t my_lock; // 1. 先定义 spin_lock_init(&my_lock); // 2. 后初始化

上锁

上锁

函数

调度/睡眠

备注

spin_lock

忙等待

只能在进程上下文使用,不能用在中断上下文

spin_lock_irq

忙等待

需要禁用中断,中断状态会被覆盖

spin_lock_irqsave

忙等待

保存中断状态并禁用本地中断,推荐使用

spin_trylock

非阻塞

尝试获取自旋锁,如果获取失败则返回

spin_trylock_irq

非阻塞

跟spin_lock_irq一样,获取失败返回

spin_trylock_irqsave

非阻塞

跟spin_trylock_irqsave一样,获取失败返回

解锁

解锁

函数

备注

spin_unlock

释放锁

spin_unlock_irq

释放锁并恢复本地中断

spin_unlock_irqrestore

释放锁,恢复中断状态,恢复本地中断,推荐使用

注意事项

  • 持有自旋锁期间绝对不能休眠
spin_lock(&lock); // ❌ 错误:以下行为都会睡眠 kmalloc(..., GFP_KERNEL); // 可能睡眠 copy_from_user(); // 可能缺页睡眠 msleep(100); // 显式睡眠 mutex_lock(&mutex); // 可能睡眠 spin_unlock(&lock);
  • 中断上下文使用自旋锁需要关中断

如果锁可能在中断处理程序中被获取,那么在进程上下文中获取该锁时,必须先禁用本地中断

// ❌ 错误:可能死锁 spin_lock(&lock); // 此时中断发生,中断处理程序也尝试 spin_lock(&lock) // 进程上下文持有锁,中断等待锁 → 死锁 // ✅ 正确 spin_lock_irqsave(&lock, flags); /* 临界区,此时不会被打断 */ spin_unlock_irqrestore(&lock, flags);
  • 自旋锁不能递归
void func1(void) { spin_lock(&lock); func2(); // func2 也尝试获取 lock → 死锁! spin_unlock(&lock); } void func2(void) { spin_lock(&lock); // ❌ 已经持有,再次获取 → 死锁 spin_unlock(&lock); }

内核推荐使用spin_lock_irqsavespin_unlock_irqrestore这两个加锁和解锁函数,这样无论是在进程上下文还是中断上下文都可以安全使用自旋锁

互斥锁

互斥锁是一种睡眠等待的锁机制。当一个线程尝试获取已被其他线程持有的互斥锁时,它不会像自旋锁那样"自旋"空耗CPU,而是主动让出CPU进入睡眠状态,直到锁被释放后被唤醒。

互斥锁初始化

互斥锁初始化有两种初始化,分别是静态初始化和动态初始化

// 直接定义并初始化一个名为 my_mutex 的互斥锁 DEFINE_MUTEX(my_mutex); struct mutex my_mutex; // 1. 先定义 mutex_init(&my_mutex); // 2. 后初始化

上锁

上锁

函数

备注

mutex_lock

如果锁被持有,则进行睡眠,不可被中断

mutex_lock_interruptible

如果锁被持有,则进行睡眠,可被信号打断

mutex_trylock

如果锁被持有,则立刻返回,不睡眠

解锁

解锁

函数

备注

mutex_unlock

只有一个释放互斥锁的函数

注意事项

  • 持有互斥锁期间不能休眠

如果持有锁的线程睡眠,其他等待该锁的线程也会被阻塞,可能导致长时间的等待链,严重影响系统性能,甚至引发优先级反转

  • 中断上下文不能使用互斥锁

因为中断上下文不属于任何进程或者线程,不能进行休眠和调度,调用mutex_lock会导致内核崩溃。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 9:25:07

2026年谷歌SEO还能做什么?我把官方指南拆成了9条人话

2026年5月,Google发布了一份文件,标题是《如何针对Google搜索的生成式AI功能优化你的网站》。这是Google第一次以官方口径告诉所有人——你的网站要被 AI Overviews 和 AI Mode 引用,到底该做什么、不该做什么。7月10号又更新了一次。加上Goo…

作者头像 李华
网站建设 2026/7/31 9:24:00

ARM架构开发实战:从核心原理到环境搭建与问题解决

1. ARM架构:从移动心脏到计算世界的“搅局者” 如果你最近几年关注过手机芯片、苹果的Mac电脑,或者折腾过树莓派、玩过NAS,那么“ARM架构”这个词一定不会陌生。它不再是那个只藏在手机和嵌入式设备里的幕后英雄,而是正以一种前所…

作者头像 李华
网站建设 2026/7/31 9:23:19

Linux服务器通过aliyundrive-webdav与rclone实现阿里云盘自动化文件同步

1. 项目概述与核心价值 在服务器运维、开发部署或者个人NAS的场景里,我们经常需要和云端存储打交道。你可能遇到过这样的窘境:手头有一台性能强劲的Linux服务器,上面跑着数据库备份、日志文件或者刚编译好的程序包,急需把它们传到…

作者头像 李华
网站建设 2026/7/31 9:22:41

Qoder自动化额度重置工具:邮件触发与API调用的完整实现方案

这次我们来看一个名为 Qoder 的项目,它似乎与额度重置或某种自动化处理相关。从标题的表述来看,Qoder 可能是一个能够帮助用户在特定场景下(比如深夜收到邮件时)快速响应、自动处理额度重置任务的工具或脚本。这类项目通常面向开发…

作者头像 李华
网站建设 2026/7/31 9:18:52

如何用3个步骤轻松备份QQ空间:GetQzonehistory终极数字记忆保存方案

如何用3个步骤轻松备份QQ空间:GetQzonehistory终极数字记忆保存方案 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾担心那些珍贵的QQ空间说说会随着时间流逝而消失…

作者头像 李华
网站建设 2026/7/31 9:17:11

Simulink锁相环PLL建模与调试实战:从原理到电机控制与并网应用

1. 从“锁不住”的困惑到“锁得稳”的实践:我的PLL仿真入门心路 如果你刚开始接触电力电子、电机控制或者通信系统,大概率会碰到一个绕不开的“拦路虎”——锁相环。我第一次在论文里看到PLL这三个字母时,感觉它神秘又强大,仿佛一…

作者头像 李华