Linux内核RCU(Read Copy Update)锁简析

简介:

在很早以前,大概是2009年的时候,写过一篇关于Linux RCU锁的文章《RCU锁在linux内核的演变》, 现在我承认,那个时候我虽然懂了RCU锁,但是我没有能力用一种非常简单的描述把Linux的实现给展示出来,有道是你能给别人用你自己的方式非常简洁地 描述清楚,你才是真正的精通它,否则,无异于背诵。换个说法,如果你在被面试,在短时间内靠嘴说给面试官,且他还要能听明白,就说明自己真的懂了,这种时 候,是不会给你机会分析源代码的,也不可能让你背诵源代码。
       时隔五年多,近期又碰到了这个话题,我不能自诩自己对RCU锁是多么精通,但是起码,和2009年相比,我确实有所进步,因此在这个台风肆虐的次日,我尝试着用我自己的方式描述一下Linux对RCU锁的一种实现方式,作为《RCU锁在linux内核的演变》这篇文章的补充。本文不配图,没代码,只是文字。
声明:如果你还不知道RCU锁是什么,请自行baidu,本文不再赘述概念,但是这也就等于说,如果我自己有一天忘记了RCU,我也不能指望从本文中得到任何帮助,我总是这样,不是吗?能力在忘不在记,得其义而忘其形。

RCU要素

RCU锁的要素包括

读标志

如果一个Reader企图占据一把RCU锁,它是不需要付出任何代价的,只需要设置一个标志,让外界知道有Reader在占据这把RCU锁,多个Reader可以共同持有一把RCU锁。

写时拷贝

如果有一个Write企图更新RCU锁所保护的数据,那么它会首先查看该RCU锁的读标志,如果有该标志,说明有最少一个Reader持有了该RCU锁,它需要对原始数据make a copy,写这个副本并将更新过的副本保存在某处,等待时机用该副本更新原始数据。

更新时机

这 个时机就是用副本更新原始数据的时间点,这个时间点如何确定是RCU锁实现的算法核心,它直接可以确定所有的数据结构。确切来讲,Writer必须 waitting for all readers leaving,方可Update原始数据,问题是,它是怎么知道所有的Reader都离开了呢?

Linux内核对RCU锁的几种实现

1.原始实现-利用抢占禁止

Linux 内核于2.6内核引入了RCU锁的概念,在第一个版本中,它利用了抢占禁止的方式来标志有Reader持有RCU锁,这意味着期间不能发生task切换 (指的是task_struct所代表的sched entity切换)。那么所有Reader均已经释放RCU锁的标志就是,task切换了,因此很简单,用副本Update原始数据的时机就是task切 换时。
       所有的Write会将自己写的副本挂在一个list上,在task切换的时候会touch这个list,如果该list非空,则遍历每一个元素,Update原始数据。

评价[该部分与实现无关,纯形而上的,可以忽略]      

这就是第一版的原始实现,它是否合理姑且不论,确实,它可以工作。但是:
a.它这种实现是否会影响调度子系统的时延
b.由于禁用抢占,抢占粒度变粗,对交互性是否会有影响
c.对CPU间的task负载均衡的影响呢
我 们发现,由于RCU的这个实现不是靠自身机制实现的,它不可避免地会影响到系统的核心机制,比如调度,负载均衡等,这意味着它不能长久,也无法经历复杂的 演变,因为随着它在这条路上的逐步演进,对系统核心机制的影响将越来越大,故而,它必须从系统层面剥离出来。确实,它也是这么做的,这就是第二代RCU实 现-可抢占RCU锁。

2.新实现-利用阶段计数器

需要一种更加有效的方式来标志Reader已经持有锁-第一要素读标志,并且这个标志要尽可能精确,且不能使用系统核心的机制,要做成完全封闭的闭环,不依靠外部当然也就不会影响外部。
       纯天然的想法就是使用计数器,每一把RCU持有一个Reader计数器,一旦有Reader前来持锁,只需要一个原子操作,将该计数器加1即 可,Writer写数据时,发现计数器不是0就意味着需要make a copy了-第二要素写时拷贝(COW-Copy On Write)。现在的问题是第三要素,Writer怎么知道所有的Reade都已经将锁释放了呢??
       纯天然的想法就是在某个Reader释放锁的时候,计数器减1,当计数器重新变为0的时候,这就是副本更新原始数据的时机。确实是这样,但是按照持锁和解 锁的分布看,它们应该是均等的,这意味着计数器的值会在一个期望值上下波动,变成0的希望及其渺茫,因此需要引入另一个参量,即阶段。
       将唯一的那个RCU计数器分裂为两个计数器:old readers和new readers。
       太初,任选某一个时刻,将RCU锁当前的计数器(称为原始计数器)值复制一份存入old readers,计数器清0,原始计数器改称为new readers。复制结束的当下,new readers计数器为0,old readers计数器为现阶段持有锁的reader的数量。并且持锁者task(即task_struct)与RCU锁之间保持关联(难道不是一个 task_struct字段可以搞定的吗?),task永远知道自己是new reader还是old reader。
       此时,就可以明确定义lock和unlock的行为了:
lock--设置自己的task为new reader,将RCU的new reader计数器加1。
unlock--获取自己的task是new reader还是old reader,将自己所在的reader计数器减1。
此时很明确的事实是,old reader计数器总是会递减而不会递增,而new reader不但会递增也会递减,这样,选择Update的时机也很明确了,那就是,old reader计数器变为0,这个时刻,就该将所有的副本覆盖原始数据了。
       现在总结所有的三个要素:

读标志

为该RCU锁的new reader计数器加1

写时拷贝

如果该RCU锁的old reader计数器不为0,则执行写时复制。

更新时机

每次unlock操作,都会将本task的reader计数器(或者是new reader,或者是old reader)减1,一旦该RCU锁的old reader计数器变成0,则执行所有的Update操作。

评价[该部分与实现无关,纯形而上的,可以忽略]

持有RCU锁的reader,可以睡眠,可以被抢占,可以调度到别的CPU上,完全是封闭的,和系统其它的机制无关。然而,我一直在思考一个更好的实现,只因疯子不给力!!

3.RCU Tree实现(实在是没有2好)

今天实在没有时间了,要出去。后续补充。




 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1673351
相关文章
|
6天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
29 4
|
8天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
30 9
|
7天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
26 6
|
8天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
27 5
|
8天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
8天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
8天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
25 2
|
Linux 网络安全 数据安全/隐私保护
linux之间copy传输文件方法
不同的Linux之间copy文件通常有4种方法 1.ftp 2.samba服务 3.sftp 4.scp 前三种方法都比较繁琐,最简单的方法就是scp 命令scp 介绍 scp 本地用户名@IP地址:文件名1 远程用户名@IP地址:文件名2   [本地用户名@IP地址:] 可以不输入,可能需要输入远程用户名所对应的密码.   可能有用的几个参数:   -v 和大多数li
2420 0
|
6天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
25 3
|
6天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
19 2