Linux驱动必学知识点-内核互斥锁

原创 一口Linux 2024-06-18 21:35

点击左上方蓝色“一口Linux”,选择“设为星标

第一时间看干货文章 

【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式知识点-思维导图-免费获取
【就业】一个可以写到简历的基于Linux物联网综合项目
【就业】找工作简历模版



 

互斥体概述

信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,mutex用于互斥操作。信号量的count初始化为1,down()/up()也可以实现类似mutex的作用。

mutex的语义相对于信号量要简单轻便一些,在锁争用激烈的测试场景下,mutex比信号量执行速度更快,可扩展性更好,另外mutex数据结构的定义比信号量小。

mutex的优点

  1. mutex和信号量相比要高效的多:
  2. mutex最先实现自旋等待机制
  3. mutex在睡眠之前尝试获取锁
  4. mutex实现MCS所来避免多个CPU争用锁而导致CPU高速缓存颠簸现象。

mutex的使用注意事项:

  1. 同一时刻只有一个线程可以持有mutex。
  2. 只有锁持有者可以解锁。不能再一个进程中持有mutex,在另外一个进程中释放他。
  3. 不允许递归地加锁和解锁。
  4. 当进程持有mutex时,进程不可以退出。
  5. mutex必须使用官方API来初始化。
  6. mutex可以睡眠,所以不允许在中断处理程序或者中断下半部中使用,例如tasklet、定时器等。

目录:

/linux/include/linux/mutex.h

/*
 * Simple, straightforward mutexes with strict semantics:
 *
 * - only one task can hold the mutex at a time
 * - only the owner can unlock the mutex
 * - multiple unlocks are not permitted
 * - recursive locking is not permitted
 * - a mutex object must be initialized via the API
 * - a mutex object must not be initialized via memset or copying
 * - task may not exit with mutex held
 * - memory areas where held locks reside must not be freed
 * - held mutexes must not be reinitialized
 * - mutexes may not be used in hardware or software interrupt
 *   contexts such as tasklets and timers
 *
 * These semantics are fully enforced when DEBUG_MUTEXES is
 * enabled. Furthermore, besides enforcing the above rules, the mutex
 * debugging code also implements a number of additional features
 * that make lock debugging easier and faster:
 *
 * - uses symbolic names of mutexes, whenever they are printed in debug output
 * - point-of-acquire tracking, symbolic lookup of function names
 * - list of all locks held in the system, printout of them
 * - owner tracking
 * - detects self-recursing locks and prints out all relevant info
 * - detects multi-task circular deadlocks and prints out all affected
 *   locks and tasks (and only those tasks)
 */

struct mutex {
  /* 1: unlocked, 0: locked, negative: locked, possible waiters */
  atomic_t    count;
  spinlock_t    wait_lock;
  struct list_head  wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
  struct task_struct  *owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
  void      *spin_mlock;  /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
  const char     *name;
  void      *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
  struct lockdep_map  dep_map;
#endif
};


作用及访问规则:

  1. 互斥锁主要用于实现内核中的互斥访问功能。内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见的。
  2. 对它的访问必须遵循一些规则:同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行解锁。互斥锁不能进行递归锁定或解锁。一个互斥锁对象必须通过其API初始化,而不能使用memset或复制初始化。一个任务在持有互斥锁的时候是不能结束的。互斥锁所使用的内存区域是不能被释放的。使用中的互斥锁是不能被重新初始化的。并且互斥锁不能用于中断上下文。
  3. 互斥锁比当前的内核信号量选项更快,并且更加紧凑。

互斥体的使用

初始化

静态定义如下:

DEFINE_MUTEX(name);

动态初始化mutex,如下:

mutex_init(&mutex);

具体实现如下:

#define mutex_init(mutex) \
do {       \
 static struct lock_class_key __key;  \
       \
 __mutex_init((mutex), #mutex, &__key);  \
} while (0)


void
__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
{
 atomic_set(&lock->count, 1);
 spin_lock_init(&lock->wait_lock);
 INIT_LIST_HEAD(&lock->wait_list);
 mutex_clear_owner(lock);
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
 lock->spin_mlock = NULL;
#endif

 debug_mutex_init(lock, name, key);
}

申请互斥锁

mutex操作列表如下:

方法描述
mutex_lock(struct mutex*)为指定的mutex上锁,如果不可用则睡眠
mutex_unlock(struct mutex*)为指定的mutex解锁
mutex_trylock(struct mutex*)视图获取指定的mutex,如果成功则返回1;否则锁被获取,返回值是0
mutex_is_lock(struct mutex*)如果锁已被征用,则返回1;否则返回0

mutex的简洁性和高效性源自于相比使用信号量更多的受限性。它不同于信号量,因为mutex仅仅实现了Dijkstra设计初衷中的最基本的行为。因此mutex的使用场景相对而言更严格。

(1)代码:linux/kernel/mutex.c

void inline fastcall __sched mutex_lock(struct mutex *lock);   
 //获取互斥锁。

实际上是先给count做自减操作,然后使用本身的自旋锁进入临界区操作。首先取得count的值,在将count置为-1,判断如果原来count的置为1,也即互斥锁可以获得,则直接获取,跳出。否则进入循环反复测试互斥锁的状态。在循环中,也是先取得互斥锁原来的状态,在将其之为-1,判断如果可以获取(等于1),则退出循环,否则设置当前进程的状态为不可中断状态,解锁自身的自旋锁,进入睡眠状态,待被在调度唤醒时,再获得自身的自旋锁,进入新一次的查询其自身状态(该互斥锁的状态)的循环。

(2)具体参见linux/kernel/mutex.c

int fastcall __sched mutex_lock_interruptible(struct mutex *lock)

和mutex_lock()一样,也是获取互斥锁。在获得了互斥锁或进入睡眠直到获得互斥锁之后会返回0。如果在等待获取锁的时候进入睡眠状态收到一个信号(被信号打断睡眠),则返回_EINIR。

(3)具体参见linux/kernel/mutex.c

int fastcall __sched mutex_trylock(struct mutex *lock);

试图获取互斥锁,如果成功获取则返回1,否则返回0,不等待。

释放互斥锁

具体参见linux/kernel/mutex.c

void fastcall mutex_unlock(struct mutex *lock);

释放被当前进程获取的互斥锁。该函数不能用在中断上下文中,而且不允许去释放一个没有上锁的互斥锁。

互斥锁试用注意事项

  1. 任何时刻中只有一个任务可以持有mutex, 也就是说,mutex的使用计数永远是1
  2. 给mutex锁者必须负责给其再解锁——你不能在一个上下文中锁定一个mutex,而在另 一个上下文中给它解锁。这个限制使得mutex不适合内核同用户空间复杂的同步场景。最 常使用的方式是:在同一上下文中上锁和解锁。
  3. 递归地上锁和解锁是不允许的。也就是说,你不能递归地持有同一个锁,同样你也不能再去解锁一个已经被解开的mutex
  4. 当持有一个mutex时 ,进程不可以退出
  5. mutex不能在中断或者下半部中使用,即使使用mutex_trylock()也不行
  6. mutex只能通过官方API管理:它只能使用上节中描述的方法初始化,不可被拷贝、手动 初始化或者重复初始化

信号量和互斥体

互斥体和信号量很相似,内核中两者共存会令人混淆。所幸,它们的标准使用方式都有简单规范:除非mutex的某个约束妨碍你使用,否则相比信号量要优先使用mutex。当你写新代码时,只有碰到特殊场合(一般是很底层代码)才会需要使用信号量。因此建议 选mutex。如果发现不能满足其约束条件,且没有其他别的选择时,再考虑选择信号量

自旋锁和互斥体使用场合

了解何时使用自旋锁,何时使用互斥体(或信号量)对编写优良代码很重要,但是多数情况下,并不需要太多的考虑,因为在中断上下文中只能使用自旋锁,而在任务睡眠时只能使用互斥体。

下面总结一下各种锁的需求情况

需求建议的加锁方法
低开销加锁优先使用自旋锁
短期锁定优先使用自旋锁
长期锁定优先使用互斥体
中断上下文中加锁使用自旋锁
持有锁需要睡眠使用互斥体

互斥锁锁定和解锁使用实例

使用方法如下:

1. struct mutex mutex;
2. mutex_init(&mutex); /*定义*/
3. //加锁
4. mutex_lock(&mutex);
5.  
6. //临界区
7. 
8. //解锁
9. mutex_unlock(&mutex);

可以看出,互斥体就是一个简化版的信号量,因为不再需要管理任何使用计数。

下面网卡DM9000的驱动,其中写入eeprom的操作试用了mutex机制:

static void
dm9000_write_eeprom(board_info_t *db, int offset, u8 *data)
{
 unsigned long flags;

 if (db->flags & DM9000_PLATF_NO_EEPROM)
  return;

 mutex_lock(&db->addr_lock);

 spin_lock_irqsave(&db->lock, flags);
 iow(db, DM9000_EPAR, offset);
 iow(db, DM9000_EPDRH, data[1]);
 iow(db, DM9000_EPDRL, data[0]);
 iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
 spin_unlock_irqrestore(&db->lock, flags);

 dm9000_wait_eeprom(db);

 mdelay(1); /* wait at least 150uS to clear */

 spin_lock_irqsave(&db->lock, flags);
 iow(db, DM9000_EPCR, 0);
 spin_unlock_irqrestore(&db->lock, flags);

 mutex_unlock(&db->addr_lock);
}

可以看到每次驱动向eeprom写入数据(访问临界资源),都需要首先获得该资源对应的互斥锁db->addr_lock,并且使用完毕必须释放该锁。

end



一口Linux 


关注,回复【1024】海量Linux资料赠送


精彩文章合集

文章推荐

【专辑】ARM
【专辑】粉丝问答
【专辑】所有原创
专辑linux入门
专辑计算机网络
专辑Linux驱动
【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式所有知识点-思维导图


一口Linux 写点代码,写点人生!
评论
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 163浏览
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 442浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 432浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 458浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 457浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 486浏览
  •   在信号处理过程中,由于信号的时域截断会导致频谱扩展泄露现象。那么导致频谱泄露发生的根本原因是什么?又该采取什么样的改善方法。本文以ADC性能指标的测试场景为例,探讨了对ADC的输出结果进行非周期截断所带来的影响及问题总结。 两个点   为了更好的分析或处理信号,实际应用时需要从频域而非时域的角度观察原信号。但物理意义上只能直接获取信号的时域信息,为了得到信号的频域信息需要利用傅里叶变换这个工具计算出原信号的频谱函数。但对于计算机来说实现这种计算需要面对两个问题: 1.
    TIAN301 2025-01-14 14:15 88浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 478浏览
  • 随着全球向绿色能源转型的加速,对高效、可靠和环保元件的需求从未如此强烈。在这种背景下,国产固态继电器(SSR)在实现太阳能逆变器、风力涡轮机和储能系统等关键技术方面发挥着关键作用。本文探讨了绿色能源系统背景下中国固态继电器行业的前景,并强调了2025年的前景。 1.对绿色能源解决方案日益增长的需求绿色能源系统依靠先进的电源管理技术来最大限度地提高效率并最大限度地减少损失。固态继电器以其耐用性、快速开关速度和抗机械磨损而闻名,正日益成为传统机电继电器的首选。可再生能源(尤其是太阳能和风能
    克里雅半导体科技 2025-01-10 16:18 319浏览
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 439浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦