LWN: Lockless编程模式 - 最后的一些话题!

COMSOL新手必看:手把手教你搭建锂电池P2D模型(附完整参数设置) 本文为COMSOL新手提供了搭建锂电池P2D仿真模型的详细指南。文章从模型原理入手,逐步讲解软件设置、几何构建、关键参数配置及后处理分析,并探讨了如何将仿真结果与智能电池管理中的SOC、SOH估算相结合,为电池性能分析和BMS算法开发提供实用洞见。 阅读详情

关注了就能看到更多这么棒的文章哦~

Lockless patterns: some final topics

March 29, 2021
This article was contributed by Paolo Bonzini
Lockless patterns
DeepL assisted translation
https://lwn.net/Articles/850202/

到目前为止,本系列已经介绍了 Linux 内核中常见的五种 lockless 模式,应该是在 Linux 上工作时最可能遇到的五种模式。在整个系列中,为了清楚起见,有些细节被遗漏了,有些被简化了。在这最后一篇文章中,我会梳理一下其中的一些细节,并尝试回答一个可能是最重要的问题:应该在什么情况下使用这里介绍的 lockless 模式?

Data, address, and control dependencies

迄今为止所有介绍过的代码示例中,在同一个线程中的指令的顺序要么是通过 acquire 和 release 语义来确保的,要么是通过 memory barrier 确保的。然而,有时候处理器和或者编译器完全不可以对指令进行 reorder(重新排序),因为某条指令是跟前面的另一条指令有依赖性的。这种情况下,哪怕是对那些 relaxed load 和 store 操作,也需要保证这两个指令之间的顺序。这里会出现三种依赖情况。

Data depenencies(数据依赖) :某个 store 操作是要将之前的一个 load 操作所读出的数据进行写入时,或者是需要使用这个 load 出来的数据时,就存在数据依赖性:

int x = READ_ONCE(a);
WRITE_ONCE(b, x + 1);

Data dependency 是这三种情况中最简单的一种,在 load 之前,不可能执行 store,否则处理器根本不知道应该写入的值是什么。因此,在这种情况下从 a 中进行的读操作其实就跟 load-acquire 一样,或者说至少对于接下来的这句 WRITE_ONCE() 操作是这样的。与 smp_load_acquire() 或 smp_rmb()不同的是,利用 data dependency 所得到的 order 顺序并不能保证接下来所有的 memory 操作都在这个 read 操作之后进行。在下面的例子中,对 c 进行的 read 可能会在对 a 的 read 之前就执行了,但是 b 的 write 操作不可能在读 a 之前进行:

int x = READ_ONCE(a);
WRITE_ONCE(b, x + 1);
int y = READ_ONCE(c);

然而,我们已经看到,大多数情况下,lockess 代码只关心特定的几个内存操作之间的排序,在某些情况下,load 操作完全可以放心地使用 READ_ONCE() 来进行,因为唯一会用到这个 load 出来的值的地方就是后面的一个 store 操作,并且这也是唯一一个需要确保顺序的操作。不过,在本系列文章中所介绍的各种模式中,都不是这种情况,因为都不是在同一个 thread 中先进行 load 再进行 store 操作的,所以无法用到这里说到的 data dependency。

Address dependency :如果一个读或写操作的 address 是来自于之前的 load 操作,或者是基于之前 load 结果算出来的,那么就存在地址依赖性。

int x = READ_ONCE(a);
int y = READ_ONCE(b[x]);

struct s *x = READ_ONCE(a);
WRITE_ONCE(x->f, 1);

address dependency 的道理也是很容易理解的。在第一个例子中,b[x]的 read 操作不可能在 x 的 read 之前进行;在第二个例子中,在读取完成之前 CPU 也是不能知道后续要写入的地址的。

同样,在下面来自第一部分的这段代码中,datum->x 的地址必须是在从 message 中通过 load 得到 datum 之后才能知道的:

thread 1                            thread 2
--------------------------------    ------------------------------------
a.x = 1;
smp_store_release(&message, &a);    datum = smp_load_acquire(&message);
                                    printk("%x\n", datum->x);

在不知道目标地址的时候,CPU 怎么可能从内存中 load 或者 store 数据呢?因此,人们会希望将 thread 2 的第一行写成下面这个样子,这样就不需要 load-acquire 指令了:

datum = READ_ONCE(message)

这个想法很合理。查看汇编代码的话,其实对于几乎所有 Linux 所能支持的处理器来说也都是成立的,不过,Alpha 架构除外,因为它的 cache 架构很特殊。从 Linux 4.15 开始,Linux 开发者决定在 Alpha 上允许 READ_ONCE() 的开销更高。因此现在 address dependency 的 load 总是会在它们所依赖的 load 之后执行。请注意,address-dependent store 从来不会出问题。

最后,*control dependency* :也就是 read 出来的值可能会导致后续的 read 或 write 操作完全不会执行的话,这就是控制依赖:

int y = 0;
if (READ_ONCE(a))
    y = READ_ONCE(b);        // *no* ordering here

if (READ_ONCE(a))
    WRITE_ONCE(b, 1);        // write is ordered here

当一个 load 与另一个 load 操作之间存在 control dependency 关系时(如上面第一个例子),就绝不会出现顺序错误的情况。CPU 可能会在执行判断逻辑(test)之前先预测性地执行后面的 load 操作,然后如果发现 "then" 这个分支的代码不应该被执行,那么直接忽略这个 load 出来的数据就好。从 load 到 store 的 control dependency 关系则是比较棘手的。这里 CPU 不是问题,因为在知道是不是真的需要进行 store 操作之前,不会把数据写入 cache,但编译器这边可能出现问题。想一想下面这样的代码:

x = READ_ONCE(a);
smp_store_release(&b, 1);
if (x)
    do_something();
else
    do_something_else();

聪明的程序员可能想这样写:

x = READ_ONCE(a);
if (x) {
    WRITE_ONCE(b, 1);
do_something();
} else {
    WRITE_ONCE(b, 1);
do_something_else();
}

这里的 control dependency 确实能让 CPU 保证 read a 和 write b 之间的顺序。然而,编译器可能会在优化时决定提前到判断条件之前就进行 write 操作,然后才会对 x 的值进行判断,这样导致生成的代码其实看起来像这个逻辑:

x = READ_ONCE(a);
WRITE_ONCE(b, 1);
if (x)
    do_something();
else
    do_something_else();

这样一来 control dependency 就不复存在了,CPU 和编译器都可能会在 read a 之前就执行了 store 操作。通常情况下,要想解决这些 control dependency 问题,只要避免这么使用并且将每个 store 操作都改为相应的 acquire 或 release 的 store 就可以了。

在上述所有这些情况中,address-dependent load 可能是你唯一会真正碰到的情况。最常见(并且很容易理解)的情况就是利用 rcu_dereference() 和 srcu_dereference() 来取得一个数据结构 struct,然后读取其中的内容。在 Alpha 架构上,这些 RCU 和 SRCU 的基本函数哪怕是在 Linux 4.15 之前版本上都已经带有了必须的 memory barrier 操作。但是,在有些很少见的情况下如果没有使用 RCU,并且两个 memory access 都是用 READ_ONCE(),那么您需要多加小心。

Optimized memory barriers

第 5 部分文章中介绍了 atomic read-modify-write 操作,比如 atomic_inc()。Linux 对于这些不会返回数值的操作都定义为 relax 语义。并且如果 compare-and-swap 操作失败了,那么也就不能确保带有 memory barrier,而成功的 compare-and-swap 操作从效果上来说就像开发者在这个操作的两边都加了一个 memory barrier 一样。

然而,有些处理器并没有一个 relaxed read-modify-write 操作执行。在这些处理器上,写这样的代码(这是第三篇文章中的 full memory barrier 模式的一个变体)就是个浪费了,这里 set_bit()(这个操作一定会先读取目标地址的数据之后才会改变指定的 bit)和 smp_mb() 已经提供了 back-to-back memory barrier:

set_bit(&flag1, FLAG_DONT_SLEEP);
smp_mb();
if (READ_ONCE(wake_me))
    wake(thread2);

为此,Linux 定义了一些优化版的 memory barrier 操作:smp_mb__before_atomic() 和 smp_mb__after_atomic() 。它们会根据当前体系架构中 atomic read-modify-write 操作的具体硬件实现细节,来决定是编译成 compiler-only barrier 还是 full memory barrier。举例来说,x86 架构上的所有 read-modify-write 操作都会在两边隐含了一个 barrier,因此在 x86 架构上,这些优化版的 memory barrier 操作就会是一个 compiler-only barrier。而在 ARM 架构上有可能会定义有 relaxed read-modify-write 操作,因此,这些优化版的 memory barrier 将生成一个 dmb 指令,从而替代 smp_mb():

set_bit(&flag1, FLAG_DONT_SLEEP);
smp_mb__after_atomic();
if (READ_ONCE(wake_me))
    wake(thread2);

另一个优化版的 memory barrier 是 smp_store_mb(),是用来替代 WRITE_ONCE() 之后紧跟着 smp_mb() 的情况。例如:

thread 1                               thread 2
-------------------                    --------------------------
smp_store_mb(dont_sleep, 1);           smp_store_mb(wake_me, 1);
if (READ_ONCE(wake_me))                if (!READ_ONCE(dont_sleep))
    wake(thread2);                         sleep();

When to use lockless patterns?

尽管本系列的前几篇文章都尽量在举例的时候使用 Linux kernel 中的实际代码,但其实它们只是用来展示原理的。虽然这些文章中的内容应该足够让大家理解现有的代码了,但是一直很少讲解什么时候应该采用这些模式以及为什么采用这些模式。

在系列文章中,我一直试图强调的一件事是,lockless 技术并不是传统的 synchronization primitives(同步接口)的替代品。它们只是达到目的的一种手段,是用来降低同步的开销的(cacheline contention,也就是 cache 竞争,这也是某种意义上的同步操作,只是它发生在处理器内部,而不是你的代码中)。并发代码(concurrent code)中开销很昂贵的同步操作是无法彻底消除的,但可以尽量减少使用这些开销昂贵的指令,或者把将它们移出 hottest parth(也就是最常执行到的代码分支)。为此,下面这些设计要点值得注意:

  • 与现在已经在使用的 lock 和 shared data access(公共数据的访问)的交互。

  • 对公共数据(shared data)的写入频率:每当一个线程向公共位置写入数据时,所引发的 cache coherency traffic(为了保持 cache 一致性而占用总线的带宽)最终可能会导致今后的扩展瓶颈(scalability bottleneck)。

  • 进行同步的频率:运行多个线程运行的最好方式是让它们尽可能地独立执行,因为同步(synchronization)操作总会引入开销的。

比方说,你想在代码运行时收集一些统计数据,比如统计一下通过网络接口发送了多少个数据包,并且希望尽量减少这个统计工作的开销。在贸然决定用 lockless 的方式实现 counter 之前(比如用 atomic increment 指令),应该先调查一下当前发送数据包这个操作是如何确保是序列化进行的。比如说发送数据包的地方已经利用了一个 spinlock 或者 mutex,那么很可能我们做了一个花里胡哨的 lockless 的 counter 功能也无法获得什么性能上的好处:如果你确保 counter 是能放在发送数据包时所需要的一个 cacheline 中的话,那么在已经持有 lock 的情况下,对单个 memory 内容进行修改的操作几乎没有多大开销。

如果发送数据包时并没有一个必定会持有的 lock,那么 lockless 技术可能确实是会带来好处的,但这里不仅仅是一个 atomic read-modify-write 操作就能搞定的。比如说,你可以考虑使用多个 counter,这样你就可以在不用 lock 的情况下(只要这些变量是 per-CPU 的就行)或在一个早已经在用的 lock(比如每个 network queue 的保护锁)保护下来对其进行递增操作。在读取统计信息的时候,可以对这些 counter 进行 sum 计算(这种操作应该很少进行):这种解决方案避免了对公共数据的并发写入,并且不需要在 hot path 上增加任何额外同步操作。

在上面的例子中,一个粗粒度的 lock(例如对 network queue 操作进行保护的 lock)并不一定意味着会在今后的扩展(scalability)时导致问题:在一个设计为保持各个线程大部分时间独立运行的系统中,对于粗粒度 lock 的竞争通常是很罕见的,甚至完全不存在。相反,如果用细粒度锁来保护过量的公共数据访问,则会大幅增加同步成本,从而导致性能下降。

细粒度锁的开销在 read/write lock 中表现得尤为明显,这种情况下,哪怕是 read 这一方,也需要进行 write 操作才能拿到这个 lock。在编写那些高可扩展性的代码(scalable code)时,基本可以将 read/write lock 看做是 "shared/exclusive" lock,这样可以简化思考。你可以使用粗粒度的 read/write lock 来确保只有在 hot path 中才需要针对并发执行的情况进行特殊设计:那些不太频繁执行的代码为了要进行 exclusive(独占)访问而获取了锁,这种情况下所有的 lockless fast path 也完全不用考虑受这些代码的影响。Linux 的 mmap_sem 就是一个例子,Linux 会在获取 mmap_sem 信号量的同时也进行许多 page-table 操作。不过,获取 mmap_sem 进行 read 操作的开销还是比较高的,这成为了一个众所周知的问题,也就是 mmap_sem 的 scalability 问题。

如果需要对特定的数据结构进行细粒度的加锁保护,那么在一个可扩展性好的系统中,通常会只有很少的对这些数据结构进行的写入操作。你可以尝试用 seqlocks 或 RCU 等机制来代替 read/write lock,对 reader 这一边的 critical p 进行保护。当 writer(写入者)不希望耗费 synchronize_rcu() 的时间时,SRCU(可以认为它是一个 subsystem RCU,而不是 sleepable RCU)也可以成为 RCU 的一个有效替代方案。

哪怕 thread 都是独立运行的,也可能在极少数情况下是必须进行交互的。如果某个系统中每秒都需要对一个 flag 进行几千次甚至几百万次的检查,如果每次检查中都用细粒度的锁进行保护,那么无论线程之间的这些争抢指令数有多么少、争抢的发生是多么罕见,带来的开销都会变得明显。在这些情况下,在 fast path 中利用 lockless 技术就非常有价值了。

例如,你可以给每个线程设计一个请求队列来容纳所有来自其他线程的 request,并通过 single-consumer linked list(单一消费者的链表)来进行管理。对这些 request 的处理也许可以采用之前 full memory barrier 文章中所提到的 cross-thread notification(线程间通讯)方式来触发。然而,这些技巧要想真有效果,那么就必须得到整个系统的支持。换句话说,在一个以避免可扩展性瓶颈为目的的系统中,往往所出现的一些细节问题都是比较通用的问题,使用我们文章中介绍的模式通常可以有效解决这些问题。

大家在努力利用 lockless 技术来提高系统的可扩展性(scalability)时,很需要把 lock-free 和 wait-free 算法区分开来。lock-free 算法保证了系统整体能往前执行下去,但并不能保证每个线程都能正常往下执行;lock-free 算法通常都是不公平的,如果每秒的操作次数超过了某个阈值,那么有可能其中一些线程最终可能会频繁失败,从而导致 livelock。wait-free 算法额外保证了每个线程的继续执行。通常复杂度就会大增(也并不一定就会更复杂)。例如消息传递和跨线程通知都是符合 wait-free 的。

从 Linux 的 llist 相关 API 来看,llist_add() 就是 lock-free 的。在消费者这一边来看的话,llist_del_first()是 lock-free 的,而 llist_del_all()是 wait-free 的。因此,如果能预测到会有许多生产者(producer)会调用 llist_add()时发生竞争的话,那么 llist 可能就不是一个合适选择了;而使用 llist_del_all()可能比 llist_del_first()更好,除非要求时间消耗一定要是个固定时长。对于某些架构来说,指令集不允许将 read-modify-write 用在 wait-free 代码里面。如果是这样的话,llist_del_all()就只是 lock-free 的(但仍然是比较好的选择,因为它可以让消费者对共享数据结构进行的访问更加少)。

无论什么情况,最好的检查代码性能方法都是对它进行基准测试(benchmark)。尽管直觉以及一些流行 pattern 可以在设计和实现阶段给你指导,但要准备好最后被测试结果证明做错了的觉悟。

最后,我将引用 Dave Chinner 的一段优秀点评来结束这个系列文章:

这就是并发编程的艺术——仅仅知道什么是 lockless 算法是不够的,你需要了解这些算法导致的数据访问模式,以及这些访问模式何时会对软件产生限制。当然,知道什么时候不使用 lockless 算法(因为有更好的方法来减少公共数据的访问)也是这个艺术的一部分。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

手把手教你用Zynq-7100 FPGA实现100Mbps OOK信号定时同步(含完整Verilog代码) 本文详细介绍了如何在Zynq-7100 FPGA平台上实现100Mbps OOK信号的定时同步,涵盖插值算法、Gardner定时误差检测等关键技术。通过完整的Verilog代码和优化技巧,解决了50MHz外部时钟条件下的400MHz采样率同步挑战,为无线通信系统设计提供实用解决方案。 阅读详情

相关推荐

[模型部署]-[LLM]昇腾部署Qwen3-Coder-30B-A3B-Instruct

摘要:本文介绍了在Ascend NPU上部署Qwen3-Coder-30B-A3B-Instruct大模型的实践过程。通过vLLM-Ascend框架,详细说明了环境准备(CANN 8.1、PyTorch-NPU 2.5.1等)、模型下载、容器启动及服务部署步骤,包括vLLM服务的启动参数配置和接口测试方法。测试结果显示在当前2卡910B3配置下性能表现仅为8token/s(1并发),并指出流式调用时存在无法输出usage信息的问题。该方案为Ascend平台私有化部署大模型推理服务提供了参考实现。

1668

深入浅出Linux内核中的内存屏障

工程师的圣地—Linux内核, 谈谈内核的架构 5个方面分析内核架构 linux内核,进程调度器的实现,完全公平调度器 CFS 深透剖析Linux内核字符与块设备驱动程序 抽象内存模型 指令重排 每个 CPU 运行一个程序,程序的执行产生内存访问操作。在这个抽象 CPU 中,内存 操作的顺序是松散的,CPU 假定进程间不依靠内存直接通信,在不改变程序执行 结果 的推测下由自己方便的顺序执行内存访问操作。 例如,考虑下面的执行过程: { A = 1 b = 2} 这有 24 中内存访问操作的组合,每种组合

C/C++Linux、音视频、DPDK 4242

Streamlit安装避坑指南:从零到一键启动你的第一个数据可视化应用

本文提供了一份详尽的Streamlit安装避坑指南,帮助用户从零开始快速启动第一个数据可视化应用。文章深入剖析了安装过程中常见的网络超时、依赖冲突、环境变量缺失等核心问题,并给出了使用虚拟环境、配置国内镜像源、利用.whl文件离线安装等多种经过验证的解决方案,确保安装过程顺畅无阻。

weixin_29065015的博客 614

smp_load_acquire smp_store_mb 和 smp_store_release 简介

和是 Linux 内核中用于多核数据同步的关键内存屏障原语。它们通过控制指令和内存访问顺序,确保多核系统中的数据可见性和操作原子性。

六六哥的博客 747

玄机-Linux权限维持-后门

玄机-Linux权限维持-后门

m0_46439803的博客 763

29、几何形态测量学在蝴蝶翅膀研究中的应用

本博客介绍了几何形态测量学在花斑木蛱蝶(Pararge aegeria)翅膀研究中的应用,探讨了环境因素对蝴蝶翅膀大小和形状的影响。通过选择合适的地标并采用Procrustes分析方法,研究揭示了不同景观类型下翅膀形状的差异及其潜在的适应性意义。文章还讨论了发育可塑性在飞行形态变异中的作用,并比较了几何形态测量学与其他传统方法的优势。最后提出了未来研究方向,包括多因素综合分析、发育机制探索及长期生态监测,为理解蝴蝶的适应性进化提供了重要线索。

ff678的博客 107

linux READ_ONCE/WRITE_ONCE/ACCESS_ONCE和smp_store_release/smp_load_acquire宏定义实现

READ_ONCE,WRITE_ONCE和 ACCESS_ONCE 宏在linux内核中出现的频率极高。那么这三个宏到底起了什么样的作用呢? smp_store_release/smp_load_acquire又如何呢?

Amelio Ming 1005

数字IC行业笔试面试全攻略

数字IC行业迎来快速发展,国产替代加速,技术创新活跃,应用领域持续扩展,但人才缺口扩大。职业发展路径清晰,从初级到首席工程师/架构师的技术路线,或转向管理岗位。薪资待遇优厚,应届生起薪25-35W,5-10年经验可达70-110W,架构师年薪超百万。薪资受公司平台、技术方向、学历背景、项目经验和地域因素影响。AI芯片、汽车电子等热门方向溢价明显,成功流片经验尤其重要。

2301_79143213的博客 759

Nat类型检测

Determining NAT Mapping Behavior This will require at most three tests. In test I, the client performs the UDP connectivity test. The server will return its alternate address and port in

skylark的总结 5326

微搭低代码实现用户登录及注册功能

在小程序开发中,我们可以拿到微信用户的openid,openid可以作为用户的唯一标识,在查询数据的时候可以进行数据过滤实现查询自己提交的数据。那微搭中是否也有openid这个概念,可以获取用户自己提交的数据呢? 答案是肯定的,那要怎么去做呢?思路是先创建一个数据源方法,在数据源中返回openid,然后低代码中可以调用数据源的方法并且赋值给全局变量,这样在后续的业务中从全局变量就可以获取openid了,方便做数据权限的控制。 为了实现这个目的,我们从创建数据源开始。 创建数据源 我们先创建一个用户管理的数据

低代码布道师的博客 2996

【温室气体】GOSAT 数据产品

【温室气体】GOSAT 数据产品

WW、forever的博客 238

内存屏障:smp_store_release与smp_load_acquire

在多处理器系统中,CPU为了提高效率会对指令执行顺序进行重排,这可能导致共享数据访问出现意外结果。内存屏障(Memory Barrier)是解决这一问题的关键机制,而`smp_store_release`与`smp_load_acquire`是Linux内核中用于实现释放-获取(Release-Acquire)语义的重要原语。本文将从实际应用场景出发,解析这两个接口的工作原理及使用方法。 ## ...

gitblog_01037的博客 722

petalinux2022.2启动文件编译配置

petalinux2021之前的版本里,导入目录components/yocto/下的“environment-setup-aarch64-xilinx-linux” 和 “layers/core/oe-init-build-env”的环境,能使用Yocto的devtool工具。(加入下面内容,先petalinux-build下载完成后才能有源码)制作 BOOT.BIN 启动文件:petalinux-package --boot --fsbl --fpga --u-boot --force。

wangjie36的博客 1387

LWN:介绍lockless算法!

Loading [Contrib]/a11y/accessibility-menu.js关注了就能看到更多这么棒的文章哦~An introduction to lockless algor...

Linux News搬运工 1319

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

LWN: lockless编程模式 - full memory barrier!

关注了就能看到更多这么棒的文章哦~Lockless patterns: full memory barriersMarch 5, 2021This article was contribu...

Linux News搬运工 565

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

LWN: Lockless编程模式 - 更多的read-modify-write例子!

关注了就能看到更多这么棒的文章哦~Lockless patterns: more read-modify-write operationsMarch 19, 2021This articl...

Linux News搬运工 864

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

LWN: Lockless编程模式 - 介绍compare-and-swap!

关注了就能看到更多这么棒的文章哦~Lockless patterns: an introduction to compare-and-swapMarch 12, 2021This arti...

Linux News搬运工 680

LWN: lockless编程模式——relaxed access和partial memory barrier

关注了就能看到更多这么棒的文章哦~Lockless patterns: relaxed access and partial memory barriersFebruary 26, 202...

Linux News搬运工 566

LWN:凡人如何理解lockless算法?

关注了就能看到更多这么棒的文章哦~Lockless algorithms for mere mortalsByJonathan CorbetJuly 28, 2020https://l...

Linux News搬运工 664

无锁编程算法简介(An introduction to lockless algorithms)

你可知道…?LWN.net 是订阅者支持的出版物;我们依靠订户来保持整个操作的进行。请通过购买订阅并保持 LWN 在线来提供帮助。当传统的锁定原语无法使用或性能不够好时,Linux 内核会对无锁算法感兴趣。出于这个原因,他们时不时地出现在 LWN 上;促使我撰写本系列文章的最后一次提及是在去年 7 月。出现得更频繁的主题是读取-复制-更新(RCU——这些 2007 年的文章仍然高度相关)、引用计数,以及将无锁基元包装到更高级别、更容易理解的 API 中的方法。

zg_hover的专栏 677

LWN:6.7合并窗口的后半部分!

关注了就能看到更多这么棒的文章哦~The rest of the 6.7 merge windowBy Jonathan CorbetNovember 13, 2023ChatGPT translationhttps://lwn.net/Articles/949957/截至 11 月 12 日,6.7 合并窗口关闭时,已经有 15,418 个非合并变更集被合并到主线内核中。于是这一轮成为了有史以来...

Linux News搬运工 995

Linux aarch64 中对内存屏障的定义

Linux aarch64 中对内存屏障的定义

suyanqiangqq的博客 779

Linux 异步通知(信号)原理及架构

异步通知 异步通知的意思是:一旦设备就绪,则主动通知应用程序进行访问。这样,使用无阻塞IO的应用程序无需轮询的查询设备是否可访问,达到减小CPU消耗的目的。类似于硬件上的“中断”的概念,比较准确的称谓是“信号驱动的异步IO”。 信号:是在软件层次上对中断机制的模拟。 在原理上,进程接收到一个信号(软件层)<==>(硬件层)处理器接收到一个中断。 假设假设recvfrom函数是一个系统调...

The blog of TseJung 1145

内存屏障

内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。本文介绍了内存屏障对多线程程序的影响。我们将研究内存屏障与JVM并发机制 的关系,如易变量(volatile)、同步(synchronized)和原子条件式(atomic conditional)。本文假定读者已经充分掌握了相关概念和Java内存模型,不讨论并发互斥、并行机制和原子性。内存屏障用来实现并发编程中称为 可见性(v

魔幻之翼的坚持 914

【Linux 内核源码分析】内存屏障

smp_store_mb()和smp_load_acquire()分别是带屏障的存储和加载函数,用于确保存储和加载操作的顺序和一致性。内存屏障的作用是强制对内存的访问顺序进行排序,保证多线程或多核处理器下的内存访问的一致性和可见性。在这种模型下,程序无法依赖于全局的、确定性的操作执行顺序。这些序列关系的正确性和顺序一致性是通过硬件层面的内存屏障指令、缓存一致性协议和处理器乱序执行的机制来实现的。Linux内核提供了多种内存屏障,包括通用的内存屏障、数据依赖屏障、写屏障、读屏障、释放操作和获取操作等。

C/C++Linux、音视频、DPDK 2154

NAT和NAT穿透介绍

NAT和NAT穿透介绍 本文描述NAT和它带来的一些问题以及相应的解决方案。 NAT介绍 NAT(Network Address Translation)是一种机制,采用这种机制的设备通过改变一个包内的IP地址和端口,把一个域内的IP地址映射为另一个域的IP地址(通常是把一个私有地址映射为一个公网地址,反之亦然)。NAT设备所做的工作包括:在NAT的公网侧分配一个临时的端口号并通过该端口号把

5126
上一篇: LWN: COW以及它最近引出的一系列问题 - 第二部分
下一篇: LWN: 终结 /dev/kmem!
LinuxNews搬运工
博客等级 码龄7年 438粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值