0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

介绍一下Linux内核中的各种锁

嵌入式悦翔园 来源:嵌入式悦翔园 2023-05-16 14:13 次阅读

一、前言

Linux内核中有许多不同类型的锁,它们都可以用来保护关键资源,以避免多个线程或进程之间发生竞争条件,从而保护系统的稳定性和可靠性。这些锁的类型包括:互斥锁(mutex)、读写锁(rwlock)、自旋锁(spinlock)和信号量(semaphore)。今天就给大家介绍一下Linux内核中的各种锁,以及我们在实际项目中该如何选择使用哪个锁。

二、几种锁的介绍

互斥锁(mutex) 是最常用的锁,它可以保护共享资源,使得在某个时刻只有一个线程或进程可以访问它。读写锁(rwlock)则可以同时允许多个线程或进程读取共享资源,但只允许一个线程或进程写入它。自旋锁(spinlock)可以用来保护共享资源,使得在某个时刻只有一个线程或进程可以访问它,但它会使线程或进程“自旋”,直到获得锁为止。最后,信号量(semaphore)可以用来控制对共享资源的访问,以保证其他线程或进程可以安全地访问它们。

读写锁(rwlock) 是一种用于控制多线程访问共享资源的同步机制。当一个线程需要读取共享资源时,可以获取读取锁,这样其他线程就可以同时读取该资源,而不会引发冲突。当一个线程需要写入共享资源时,可以获取写入锁,这样其他线程就不能访问该资源,从而保证数据的完整性和一致性。

自旋锁(spinlock) 是一种简单而有效的用于解决多线程同步问题的锁。它是一种排他锁,可以在多线程环境下保护共享资源,以防止多个线程同时对该资源进行访问。自旋锁的基本原理是,当一个线程试图获取锁时,它会不断尝试获取锁,直到成功为止。在这期间,线程不会进入休眠状态,而是一直处于忙等待(busy-waiting)状态,这也就是自旋锁的由来。

信号量(semaphore) 是一种常用的同步机制,它可以用来控制多个线程对共享资源的访问。它有助于确保同一时间只有一个线程能够访问共享资源,从而避免资源冲突和竞争。信号量是一种整数计数器,用于跟踪可用资源的数量。当一个线程需要访问共享资源时,它首先必须获取信号量,这会将信号量的计数器减少1,而当它完成访问共享资源后,它必须释放信号量,以便其他线程也可以访问共享资源。

四、互斥锁(Mutex)

互斥锁是最基本的锁类型,在内核中使用较为广泛。它是一种二元锁,只能同时有一个线程持有该锁。当一个线程请求该锁时,如果锁已被占用,则线程会被阻塞直到锁被释放。互斥锁的实现使用了原子操作,因此它的性能比较高,但也容易出现死锁情况。

在内核中,互斥锁的定义如下:

structmutex{
raw_spinlock_twait_lock;
structlist_headwait_list;
structtask_struct*owner;
intrecursion;
#ifdefCONFIG_DEBUG_LOCK_ALLOC
structlockdep_mapdep_map;
#endif
};

互斥锁的使用非常简单,通常只需要调用两个函数即可完成:

voidmutex_init(structmutex*lock):函数用于初始化互斥锁
voidmutex_lock(structmutex*lock):函数用于获取互斥锁
voidmutex_unlock(structmutex*lock):函数用于释放互斥锁

五、读写锁(Reader-Writer Lock)

读写锁是一种特殊的锁类型,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。读写锁的实现使用了两个计数器,分别记录当前持有锁的读线程数和写线程数。

在内核中,读写锁的定义如下:

structrw_semaphore{
longcount;
structlist_headwait_list;
#ifdefCONFIG_DEBUG_LOCK_ALLOC
structlockdep_mapdep_map;
#endif
};

读写锁的使用也比较简单,通常只需要调用三个函数即可完成:

init_rwsem(struct rw_semaphore *sem):函数用于初始化读写锁
down_read(struct rw_semaphore *sem):函数用于获取读锁
up_read(struct rw_semaphore *sem):函数用于释放读锁
down_write(struct rw_semaphore *sem):函数用于获取写锁
up_write(struct rw_semaphore *sem):函数用于释放写锁

六、自旋锁(spinlock)

自旋锁是一种保护共享资源的锁,它会在等待期间一直占用CPU。自旋锁适用于代码临界区比较小的情况,且共享资源的独占时间比较短,这样就可以避免上下文切换的开销。自旋锁不能用于需要睡眠的代码临界区,因为在睡眠期间自旋锁会一直占用CPU。

在Linux内核中,自旋锁使用spinlock_t类型表示,可以通过spin_lock()和spin_unlock()函数对其进行操作。

spin_lock_init(spinlock_t*lock):用于初始化自旋锁,将自旋锁的初始状态设置为未加锁状态。
spin_lock(spinlock_t*lock):用于获得自旋锁,如果自旋锁已经被占用,则当前进程会自旋等待,直到自旋锁可用。
spin_trylock(spinlock_t*lock):用于尝试获取自旋锁,如果自旋锁当前被占用,则返回0,否则返回1。
spin_unlock(spinlock_t*lock):用于释放自旋锁。

在使用自旋锁时,需要注意以下几点:

自旋锁只适用于临界区代码比较短的情况,因为自旋等待的过程会占用CPU资源。

自旋锁不可重入,也就是说,如果一个进程已经持有了自旋锁,那么它不能再次获取该自旋锁。

在持有自旋锁的情况下,应该尽量避免调用可能会导致调度的内核函数,比如睡眠函数,因为这可能会导致死锁的发生。

在使用自旋锁的时候,应该尽量避免嵌套使用不同类型的锁,比如自旋锁和读写锁,因为这可能会导致死锁的发生。

当临界区代码较长或者需要睡眠时,应该使用信号量或者读写锁来代替自旋锁。

七、信号量(semaphore)

信号量是一种更高级的锁机制,它可以控制对共享资源的访问次数。信号量可分为二元信号量和计数信号量。二元信号量只有0和1两种状态,常用于互斥锁的实现;计数信号量则可以允许多个进程同时访问同一共享资源,只要它们申请信号量的数量不超过该资源所允许的最大数量。

在Linux内核中,信号量使用struct semaphore结构表示,可以通过down()和up()函数对其进行操作。

voidsema_init(structsemaphore*sem,intval):初始化一个信号量,val参数表示初始值。
voiddown(structsemaphore*sem):尝试获取信号量,如果信号量值为0,调用进程将被阻塞。
intdown_interruptible(structsemaphore*sem):尝试获取信号量,如果信号量值为0,调用进程将被阻塞,并可以被中断。
intdown_trylock(structsemaphore*sem):尝试获取信号量,如果信号量值为0,则立即返回,否则返回错误。
voidup(structsemaphore*sem):释放信号量,将信号量的值加 1,并唤醒可能正在等待信号量的进程。

八、该如何选择正确的锁

当需要对共享资源进行访问和修改时,我们通常需要采用同步机制来保证数据的一致性和正确性,其中锁是最基本的同步机制之一。不同类型的锁适用于不同的场景。

互斥锁适用于需要保护共享资源,只允许一个线程或进程访问共享资源的场景。例如,当一个线程正在修改一个数据结构时,其他线程必须等待该线程释放锁后才能修改该数据结构。

读写锁适用于共享资源的读写操作频繁且读操作远大于写操作的场景。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。例如,在一个数据库管理系统中,读取操作比写入操作频繁,使用读写锁可以提高系统的并发性能。

自旋锁适用于保护共享资源的访问时间很短的场景,当线程需要等待的时间很短时,自旋锁比互斥锁的性能更好。例如,在访问共享资源时需要进行一些简单的操作,如对共享资源进行递增或递减等操作。

信号量适用于需要协调多个线程或进程对共享资源的访问的场景,允许多个线程或进程同时访问共享资源,但同时访问的线程或进程数量有限。例如,在一个并发下载系统中,可以使用信号量来限制同时下载的文件数量。

举个生活中的例子:当我们在买咖啡的时候,柜台前可能会有一个小桶,上面写着“请取走您需要的糖果,每人一颗”这样的字样。这个小桶就是一个信号量,它限制了每个人能够取走的糖果的数量,从而保证了公平性。

如果我们把这个小桶换成互斥锁,那么就可以只允许一个人在柜台前取走糖果。如果使用读写锁,那么在非高峰期的时候,多个人可以同时取走糖果,但在高峰期时只允许一个人取走。

而如果我们把这个小桶换成自旋锁,那么当有人在取走糖果时,其他人就需要一直在那里等待,直到糖果被取走为止。这样可能会造成浪费时间的情况,因为其他人可能有更紧急的事情需要处理。

九、总结

在Linux内核中,有四种常见的锁:互斥锁、读写锁、自旋锁和信号量。这些锁适用于不同的场景,开发者需要根据实际情况选择适当的锁来确保并发访问的正确性和性能。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 计数器
    +关注

    关注

    32

    文章

    2255

    浏览量

    94396
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21627
  • 信号量
    +关注

    关注

    0

    文章

    53

    浏览量

    8322

原文标题:Linux内核中的互斥锁、读写锁、自旋锁、信号量该如何选择?

文章出处:【微信号:嵌入式悦翔园,微信公众号:嵌入式悦翔园】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    文搞懂Linux内核链表

    hello 大家好,今天给大家介绍一下linux 内核链表的分析,在写这篇文章前,笔者自己以前也只是停留在应用层面,没有深究其中的细节,很多也是理解的不是很透彻。写完此文后,发现对链表
    发表于 11-14 09:17 1060次阅读

    Linux内核RCU的用法

    Linux内核,RCU最常见的用途是替换读写。在20世纪90年代初期,Paul在实现通用RCU之前,实现了种轻量级的读写
    的头像 发表于 12-27 09:56 1688次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b><b class='flag-5'>中</b>RCU的用法

    请问一下内核的GPIO怎么使用?

    请问一下内核的GPIO怎么使用?
    发表于 09-18 07:03

    linux 5.4.31为例来介绍一下linux内核目录结构

    ,它是Linux内核的概述和编译命令说明。readme的说明更加针对X86等通用的平台,对于某些特殊的体系结构,可能有些特殊的地方。内核源码很复杂,包含多级目录,形成个庞大的树状结构
    发表于 02-16 07:30

    介绍一下Linux内核编译和更新的操作流程

    。由于官方没有提高最新Linux内核版本的烧写固件,为了解决些比较严重的bug,需要自行编译Linux内核进行更新,接下来就
    发表于 06-21 09:58

    详细介绍一下指纹行业的些专业术语

    的专业术语你了解多少?知道什么是“分辨率”是“拒真率”吗?什么是“误识别率”?今天小编就为大家简单介绍一下指纹行业的些专业术语,让大家在选购的时候能得心应手。 1.什么叫拒真率 “
    发表于 07-26 17:07 1429次阅读

    Linux的伤害/等待互斥介绍

    、 rw_semaphore 、 spinlock和rwlock。第次听说ww_mutex,在百度上查找的时候发现介绍文档很少,于是自己学习,写成笔记。 在某些场合必须同时持有多个,并且获取
    的头像 发表于 11-06 17:27 2636次阅读

    分析一下SR存器的原理

    作为电路设计者,存器很多场合都会用到,今天和大家分析一下SR存器的原理。
    的头像 发表于 08-20 17:30 6843次阅读
    分析<b class='flag-5'>一下</b>SR<b class='flag-5'>锁</b>存器的原理

    小编科普一下Linux内核中常用的C语言技巧

    Linux内核采用的是GCC编译器,GCC编译器除了支持ANSI C,还支持GNU C。在Linux内核,许多地方都使用了GNU C语言的
    的头像 发表于 02-08 11:51 682次阅读

    介绍一下linux内核比较优秀的调试方式KGDB

    printf相信学过C语言的同志再熟悉不过了,然而在linux内核开发中有种非常简洁的日志输出函数叫-printk。
    的头像 发表于 03-08 13:45 1817次阅读

    文搞懂Linux系统内核的重要性

    今天我要跟大家分享一下Linux内核的重要性。内核就像Linux系统运行的大心脏,对系统的运行起到了至关重要的作用。那么
    的头像 发表于 03-24 15:16 894次阅读
    <b class='flag-5'>一</b>文搞懂<b class='flag-5'>Linux</b>系统<b class='flag-5'>内核</b>的重要性

    Linux内核结构介绍

    通常情况Linux内核的结构被认为包含以下11个主要层次。
    的头像 发表于 04-14 11:59 1246次阅读

    linux内核的driver_register介绍

    linux内核注册驱动由driver_register()完成。它将驱动程序的信息添加到内核的驱动程序列表,使得内核能够在需要时与该驱动
    的头像 发表于 07-14 09:17 2726次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>内核</b><b class='flag-5'>中</b>的driver_register<b class='flag-5'>介绍</b>

    Linux内核各种介绍

    首先得搞清楚,不同的 作用对象 不同。 下面分别是作用于 临界区 、 CPU 、 内存 、 cache 的各种的归纳: 、atomic原子变量/spinlock自旋
    的头像 发表于 11-08 17:15 878次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b><b class='flag-5'>中</b>的<b class='flag-5'>各种</b><b class='flag-5'>锁</b><b class='flag-5'>介绍</b>

    Linux各种的理解

    . 出现的原因 临界资源是什么: 多线程执行流所共享的资源 的作用是什么, 可以做原子操作, 在多线程针对临界资源的互斥访问...
    的头像 发表于 11-11 15:44 526次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>下</b><b class='flag-5'>各种</b><b class='flag-5'>锁</b>的理解