linux有几种锁机制

73次阅读
没有评论

共计 3306 个字符,预计需要花费 9 分钟才能阅读完成。

这篇文章主要介绍了 linux 有几种锁机制的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇 linux 有几种锁机制文章都会有所收获,下面我们一起来看看吧。

linux 锁的种类:1、mutex(互斥锁),用于保证在任何时刻,都只能有一个线程访问该对象;2、rwlock(读写锁),分为读锁和写锁,适用于读取数据的频率远远大于写数据的频率的场合;3、spinlock(自旋锁),在任何时刻同样只能有一个线程访问对象;4、seqlock(顺序锁),用于能够区分读与写的场合,并且是读操作很多、写操作很少,写操作的优先权大于读操作。

本教程操作环境:linux7.3 系统、Dell G3 电脑。

Linux 的几种锁机制

互斥锁:mutex

  互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。

读写锁:rwlock

  读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。

注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。

适用于读取数据的频率远远大于写数据的频率的场合。

自旋锁:spinlock

  自旋锁:spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费 CPU 资源。

RCU

  RCU:即 read-copy-update,在修改数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后,再将老数据 update 成新的数据。

使用 RCU 时,读者几乎不需要同步开销,既不需要获得锁,也不使用原子指令,不会导致锁竞争,因此就不用考虑死锁问题了。而对于写者的同步开销较大,它需要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操作。在有大量读操作,少量写操作的情况下效率非常高。

信号量:semaphore

linux 内核的信号量在概念和原理上与用户态的 SystemV 的 IPC 机制信号量是一样的,但是它绝不可能在内核之外使用,因此它与 SystemV 的 IPC 机制信号量毫不相干。

信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减 1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源。当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加 1 实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。

rw_semaphore(读写信号量)

读写信号量对访问者进行了细分,或者为读者,或者为写者,读者在保持读写信号量期间只能对该读写信号量保护的共享资源进行读访问,如果一个任务除了需要读,可能还需要写,那么它必须被归类为写者,它在对共享资源访问之前必须先获得写者身份,写者在发现自己不需要写访问的情况下可以降级为读者。读写信号量同时拥有的读者数不受限制,也就说可以有任意多个读者同时拥有一个读写信号量。如果一个读写信号量当前没有被写者拥有并且也没有写者等待读者释放信号量,那么任何读者都可以成功获得该读写信号量;否则,读者必须被挂起直到写者释放该信号量。如果一个读写信号量当前没有被读者或写者拥有并且也没有写者等待该信号量,那么一个写者可以成功获得该读写信号量,否则写者将被挂起,直到没有任何访问者。因此,写者是排他性的,独占性的。
读写信号量有两种实现,一种是通用的,不依赖于硬件架构,因此,增加新的架构不需要重新实现它,但缺点是性能低,获得和释放读写信号量的开销大;另一种是架构相关的,因此性能高,获取和释放读写信号量的开销小,但增加新的架构需要重新实现。在内核配置时,可以通过选项去控制使用哪一种实现。

读写信号量:rw_semaphore

读写信号量对访问者进行了细分,或者为读者,或者为写者,读者在保持读写信号量期间只能对该读写信号量保护的共享资源进行读访问,如果一个任务除了需要读,可能还需要写,那么它必须被归类为写者,它在对共享资源访问之前必须先获得写者身份,写者在发现自己不需要写访问的情况下可以降级为读者。读写信号量同时拥有的读者数不受限制,也就说可以有任意多个读者同时拥有一个读写信号量。如果一个读写信号量当前没有被写者拥有并且也没有写者等待读者释放信号量,那么任何读者都可以成功获得该读写信号量;否则,读者必须被挂起直到写者释放该信号量。如果一个读写信号量当前没有被读者或写者拥有并且也没有写者等待该信号量,那么一个写者可以成功获得该读写信号量,否则写者将被挂起,直到没有任何访问者。因此,写者是排他性的,独占性的。

读写信号量有两种实现,一种是通用的,不依赖于硬件架构,因此,增加新的架构不需要重新实现它,但缺点是性能低,获得和释放读写信号量的开销大;另一种是架构相关的,因此性能高,获取和释放读写信号量的开销小,但增加新的架构需要重新实现。在内核配置时,可以通过选项去控制使用哪一种实现。

seqlock****(顺序锁)

用于能够区分读与写的场合,并且是读操作很多、写操作很少,写操作的优先权大于读操作。seqlock 的实现思路是,用一个递增的整型数表示 sequence。写操作进入临界区时,sequence++;退出临界区时,sequence 再 ++。

写操作还需要获得一个锁(比如 mutex),这个锁仅用于写写互斥,以保证同一时间最多只有一个正在进行的写操作。当 sequence 为奇数时,表示有写操作正在进行,这时读操作要进入临界区需要等待,直到 sequence 变为偶数。读操作进入临界区时,需要记录下当前 sequence 的值,等它退出临界区的时候用记录的 sequence 与当前 sequence 做比较,不相等则表示在读操作进入临界区期间发生了写操作,这时候读操作读到的东西是无效的,需要返回重试。

seqlock 写写是必须要互斥的。但是 seqlock 的应用场景本身就是读多写少的情况,写冲突的概率是很低的。所以这里的写写互斥基本上不会有什么性能损失。而读写操作是不需要互斥的。seqlock 的应用场景是写操作优先于读操作,对于写操作来说,几乎是没有阻塞的(除非发生写写冲突这一小概率事件),只需要做 sequence++ 这一附加动作。而读操作也不需要阻塞,只是当发现读写冲突时需要 retry。seqlock 的一个典型应用是时钟的更新,系统中每 1 毫秒会有一个时钟中断,相应的中断处理程序会更新时钟(写操作)。

而用户程序可以调用 gettimeofday 之类的系统调用来获取当前时间(读操作)。在这种情况下,使用 seqlock 可以避免过多的 gettimeofday 系统调用把中断处理程序给阻塞了(如果使用读写锁,而不用 seqlock 的话就会这样)。中断处理程序总是优先的,而如果 gettimeofday 系统调用与之冲突了,那用户程序多等等也无妨。

互斥锁和读写锁的区别:

1)读写锁区分读者和写者,而互斥锁不区分

2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

关于“linux 有几种锁机制”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“linux 有几种锁机制”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-29发表,共计3306字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)