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

关于memory barrier/memory ordering 的笔记和总结(2) Memory ordering Memory ordering用来描述系统中的processor对内存的操作如何对其它processor可见(可见的定义见前面的描述)。同时需要说明的是,大多数文献都采用reorder这个表达方式,是从执行等价的角度来描述的:比如P1上执行两个写操作WRITE(A)和WRITE(B),如果对于观察者P2来说P1|WRI 阅读详情

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

Lockless patterns: full memory barriers

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

系列文章之一:LWN:介绍lockless算法!

系列文章之二:LWN: lockless编程模式——relaxed acess和partial memory barrier

本系列前两篇文章中提到过有四种对内存访问确保顺序的方式:第一篇中的 load-acquire 和 store-release 操作,第二篇中的 read 和 write memory barriers。本系列继续探讨 full memory barrier,为什么它们开销太大,以及它们在内核中是如何使用的。

到目前为止所介绍的 4 种不同原语(primitives)都可以对 load 和 store 的顺序进行限制:

  • load-acquire 操作会在其后的 load 和 store 操作之前完成。

  • store-release 操作被排在其前的 load 和 store 之后。

  • read memory barrier 能确保其前的 load 先完成,再进行之后的 load。

  • write memory barrier 确保其前 store 先完成,再进行后续的 store。

可以看出,上面这些操作居然都不能确保之前的 store 和之后的 load 的先后顺序:

其实对处理器来说,要保证 store 能在之后的 load 之前完成,这是一个更加复杂的过程,它应该有一个自己的原语。为了给大家讲清楚原因,我们无法继续使用到目前为止一直在用的高级语言概念,而需要直接深入理解处理器是如何运作的。

How processors really do it

第一篇文章已经提到,从底层来看,处理器之间是利用消息传递架构(message-passing architecture)进行通信的,如QPI或HyperTransport。然而,在汇编语言层面,程序员看到的只是 memory load 和 store 等操作。任何与这些 memory 操作相关的 acquire 和 release 语义都是一种假象,是由处理器的执行流水线提供出来的,也是基于它所运行的代码和此处理器的体系架构的约束的。

例如,在 x86 处理器上,所有的 memory load 都算作 "load-acquire",所有的 memory store 都算作 "store-release";这种行为是(x86 这个)体系架构所要求的。在针对这些处理器进行代码编译时,仍然需要标注 acquire 和 release 操作以及插入 memory barrier,但这些标注只是给编译器看的,用来避免进行无效的优化(invalid optimization)。即使是在这个工作模式下,体系架构也不能保证所有处理器都以相同的顺序看到这些 load 和 store 的结果。比如,假设 a 和 b 已经初始化为零:

CPU 1                    CPU 2
-------------------      --------------------
store 1 into a           store 1 into b
load b into x            load a into y

如果手动排一下,将这四个操作进行各种交叉排列,你会发现至少有一个 store 是在相应的 load 之前完成的。因此,人们会认为在上述流程运行完成后,x 和 y 中至少有一个会是 1。然而,即使在 x86 处理器上,也有可能 x 和 y 读出来都是 0。

为什么会这样呢?原因就在于有 store buffer 这么个东西。store buffer 位于 CPU 与其 L1 cache 之间。到内存的 store 操作通常只会改变 cache line 中的一部分,因此为了完成这些 store,即使只是写到 cache,也可能需要先从内存中读取一个完整的 cache line,这个操作比较耗时。在这个操作过程中,要 store 的数据会先暂存在 store buffer 中,从而允许 CPU 可以继续向后执行。

即使是这些具有 store buffer 的 CPU,也可以比较容易就确保 load-load、load-store、和 store-store 的顺序:

  • 在 out-of-order 的 CPU 上(这种 CPU 可以不按照代码中出现的顺序来执行指令,以提高性能),可以通过预测执行的方式来针对之前的 load 操作来对 memory 操作进行排序。在 cache line 访问和引起这次操作访问的指令走完流水线(instruction retire)之间的这段时间里,CPU 会记录、跟踪这个 cache line 的 eviction 操作。各个指令走完流水线(retire)的顺序是保证按照代码顺序的,所以会一直跟踪直到其之前的 load 全部完成。如果在指令 retire 之前,cache line 发生了 eviction,那么预测执行就会被取消,处理器会重新发起 memory 操作。

  • 确保 store 的顺序就更简单了,只要按照 FIFO 的顺序,将 store buffer 中的数据 flush 到 cache 里去即可。

然而,确保 store-load 顺序就不那么容易了。首先,某个 CPU 所发起的 store 操作可能正停留在它的 store buffer 中,因此当另一个 CPU 从 L1 cache 中 load 数据时,看不到这个 store 进去的值。其次,store buffer 还提供了 store forwarding 的能力,也就是说,memory load 的结果会直接从 store buffer 中获取,而不是从 cache 读取。如果 CPU 1 或 CPU 2 的 store buffer 中有 b 和 a 这一项,那么 load 操作就可能取到这个 store buffer 中的值,也就是 0。

解决这些问题的唯一方法是在 store 和 load 之间确保 store buffer 被 flush 干净了。这个操作听起来很耗时(会消耗几十个时钟周期),但这就是 full memory barrier 也就是 smp_mb()所做的事情了。下面代码和上面的逻辑类似,只是修复过写成 C 语言:

thread 1                 thread 2
-------------------      --------------------
WRITE_ONCE(a, 1);        WRITE_ONCE(b, 1);
smp_mb();                smp_mb();
x = READ_ONCE(b);        y = READ_ONCE(a);

假设 x 为 0。下图中这一条从 read 到 write 的波浪线就表示 WRITE_ONCE(b,1) 覆盖了 thread 1 所 load 的值,那么情况(在内核的 memory-model 文档中有详细描述)如下。

WRITE_ONCE(a, 1);
       |
  -----+----- smp_mb();
       |
       v
x = READ_ONCE(b);   ∿∿∿∿∿∿∿>  WRITE_ONCE(b, 1);
                                     |
                                -----+----- smp_mb();
                                     |
                                     v
                              y = READ_ONCE(a);

因为这些都是 relax 操作,所以不足以在 thread 1 和 thread 2 之间确保两者先后关系。而 barrier 本身也没有 acquire 或 release 的语义,所以两个线程之间根本没有确保先后关系。

然而,这里 barrier 确实确保了两个线程中的操作的先后排序。继续以 x=0 的情况为例具体来说,thread 2 中 full memory barrier 保证在 read_once(a) 这一句执行时,store buffer 已经被 flush 了。会不会在 READ_ONCE(b) 执行之前就已经完成了呢?如果是这样的话,READ_ONCE(b)肯定会看到 thread 2 之前的 WRITE_ONCE(b,1) (毕竟 store buffer 已经被 flush 了),而 x 会是 1。这是一个矛盾,因此 READ_ONCE(b) 一定是先执行的:

WRITE_ONCE(a, 1);              WRITE_ONCE(b, 1);
       |                              |
       |                         -----+----- smp_mb();
       |                              |
       v                              v
x = READ_ONCE(b); -----------> y = READ_ONCE(a);
                    (if x=0)

由于传递性原则,READ_ONCE(a)可以看到 WRITE_ONCE(a,1)的效果,也能看到 y=1。同样,如果 thread 2 从 a 读出来是 0,那么 thread 1 的 full memory barrier 保证了 READ_ONCE(a) 一定在 thread 1 的 READ_ONCE(b)之前执行完:

WRITE_ONCE(a, 1);              WRITE_ONCE(b, 1);
       |                              |
  -----+----- smp_mb();               |
       |                              |
       v                              v
x = READ_ONCE(b); <----------- y = READ_ONCE(a);
                    (if y=0)

这里如果 y=0 就一定意味着 x=1。不同的执行流程可能会走到上述的两种可能性之一,但无论如何,x和 y 不可能都是 0,因为那意味着 read_once(a)在 read_once(b) 之前执行,反之亦然,这都是不可能出现的。

Linux 内核的 memory model并未提及这些read-read之间的"happen-before"关系,因为没有说明哪个是释放操作,哪个是获取操作。但是,实际上它们对于不同线程之间的 memory 操作顺序的确保方面,具有同样的效果。因此,对那些底层使用了 full memory barrier 的高级别 API,我们也可以认为它们具有 acquire 或 release 的语义。这些 API 的使用者就也能利用 happens-before 的方式来思考他们的代码。接下来的例子将展示如何在实践中使用。

Sleep/wake-up synchronization

上一节的详细描述,已经说明 full memory barrier 实际上以一种复杂、不直观的方式来确保了先后顺序。幸运的是,上述 "two thread and two flags" 这种模式(即每个线程向一个 flag 写入、并从另一个 flag 读取)对你来说也许就是你关于全内存壁垒所需要知道的全部了。

这个模式在很多实际的重要场景下都有应用。假设 thread 2 希望 thread 1 在 2 提出请求的时候采取行动,而此时 thread 1 想做一些其他事,甚至可能是一些不确定需要执行多长时间的工作,例如将打算退出调度器的 run queue 并进入 sleep 状态。那么,代码会是这样的:

如果 thread 2 读取 dont_sleep 得到值为 0,thread 1 读取 wake_me 就会得到 1,并唤醒 thread 2。将 thread 1 视为具有 release 语义的话就更容易理解了(可以认为 wake()就是 mutex_unlock 的一部分)。如果 thread 1 读取 wake_me 得到 0,thread 2 读取 dont_sleep 就会得到 1,于是不会进入 sleep 状态。通常,这种情况下应该是使用 acquire 语义的。

这里实际上隐藏了一个假设条件,即 thread 1 的 wake-up 这个请求永远不会丢失,也就是说,哪怕在 thread 2 的 READ_ONCE()之后但在 sleep()之前调用 wake(),这个请求也要是仍然有效的。要避免这个 bug,可以让 wake()和 sleep()的调用都使用同一个锁来保护起来。这里我们再一次看到了 lockless pattern 是如何与传统的 synchronization 合作的,毕竟加锁的这条路平常都不会走到(也就是说这是一个 slow path)。

这种方法确实有效,例如在 Linux 的 prepare_to_wait() 和 wake_up_process() API 中就可以看到效果。这组接口是在 2.5.x 内核中引入的,当时 LWN 就进行了一些介绍。下面是展开了一些相关函数后,这个模式看起来是什么样子:

正如我们在研读 seqcount 时看到的,memory barrier 被隐藏在更高级别的 API 的实现里。实际上人们定义一个带有 acquire 或者 release 语义的 API 的时候,其实就是在其内部加上一些 memory barrier 或者 load-acquire/store-release 操作。在这个案例中,wake_up_process() 就具有 release 语义,而 set_current_state()及调用它的 prepare_to_wait() 就具有 acquire 语义。

为了减少不必要的唤醒,通常会对 sleep condition 进行两次检查,就像这样:

thread 1                               thread 2
-------------------                    --------------------------
WRITE_ONCE(dont_sleep, 1);             if (!READ_ONCE(dont_sleep)) {
smp_mb();                                WRITE_ONCE(wake_me, 1);
if (READ_ONCE(wake_me))                  smp_mb();
    wake(thread2);                       if (!READ_ONCE(dont_sleep))
                                           sleep();
                                       }

在内核里,我们可以在 tcp_data_snd_check() 和调用 tcp_check_space()的地方(thread 1)和 tcp_poll()(thread 2)之间的交互过程中看到两次检查。在这种情况下,代码没能够利用更高级别的抽象,所以可以仔细研究一下。thread 2 希望在 socket 的 send buffer 没有空间的情况下 sleep,因此 tcp_poll()在检查 __sk_stream_is_writeable()之前设置了带有 "wake me" 含义的 SOCK_NOSPACE flag。tcp_poll()中lockless synchronization 的核心代码是:

set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
smp_mb__after_atomic();
if (__sk_stream_is_writeable(sk, 1))
  mask |= EPOLLOUT | EPOLLWRNORM;

如果 mask 是 0,调用这部分代码的函数就会最终进入 sleep。smp_mb__after_atomic()是 smp_mb()的一个专用版本,但是语义相同。这些用于优化过的 barrier 会在未来的文章中得到进一步解释。

而 thread 1 在处理完 send buffer 中的数据后,必须唤醒 thread 2,tcp_data_snd_check() 首先发送 packet 来从 buffer 中腾出空间("不要睡觉了,现在有空间了!"),然后检查 SOCK_NOSPACE,最后(通过 sk->sk_write_space()函数指针)调用到 sk_stream_write_space(),thread 2 就被唤醒。调用栈不算复杂,所以建议读者自己可以去研究一下代码。不过,我想强调一下 tcp_check_space()中的这段注释:

/* pairs with tcp_poll() */
smp_mb();
if (test_bit(SOCK_NOSPACE, &sk->sk_socket->flags))
  tcp_new_space(sk);

这里利用了 barrier 的 "pair" 含义,来告诉我们这个函数具有 acquire 或 release 的语义。read memory barrier 或者 write memory barrier 相当于直接告诉我们这个函数具有获取或释放的语义。对于一个 full memory barrier 来说,我们需要仔细阅读 barrier 周围的代码。具体这个例子中,我们知道该函数在 "wake-up" 这一侧,因此它就具有 release 语义;而 tcp_poll()则具有 acquire 语义。

几乎内核中所有可以找到 smp_mb()的地方,都是类似的用法。例如:

  • workqueue 使用这个方式来决定是否有更多的工作需要 worker 去做。这种情况下,thread 1 的角色就被换成 insert_work(),而 thread 2 则变成 wq_worker_sleeping()。

  • 在 futex()系统调用中,thread 1 的 write 是在 user space 发生的,而 memory barrier 和 read 操作则是 futex(FUTEX_WAKE)内部实现的。thread 2 的操作就完全是 futex(FUTEX_WAIT)的一部分(因为 wake_me 是 kernel memory 中的一个 flag);FUTEX_WAIT 会使用 futex 的预期值作为系统调用的参数,并用它来决定是否 sleep。关于这里具体是如何生效的,可以参见 kernel/futex.c 文件开头的长长的注释。

  • 在 KVM 中,没有 sleep() 行为了,而是变成了进入处理器的 guest mode 并执行虚拟机内部工作的这个行为了。为了让处理器从 guest mode 退出来,kvm_vcpu_kick() 会向处理器发送一个处理器间中断(inter-processor interrupt)。可以跟着这一串函数调用直到 kvm_vcpu_exiting_guest_mode(),在这里就能找到我们熟悉的关于 memory barrier pairing 的注释了,这个函数也是读取 vcpu->mode 的地方。

  • Virtio 设备中实际上有两处在使用我们介绍的这个模式。其中之一是在 driver 希望停止处理 completed requests,并且发送一个中断来对其进行唤醒操作。另一方面,device 想停止处理 submitted requests,device 就通过写入 "doorbell"内存位置(相当于按门铃)来唤醒它。

我们这就完成了 memory barrier 的介绍。后续,我们将讨论一下 compare-and-swap 操作、它们如何与锁结合起来实现 lock-free fast path、以及它们在实现 lock-free linked list 中起到的作用。

全文完

参考资料:


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

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

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

NXP应用随记(八):S32K3XX的HSE学习记录(HSE\MU\UTEST\IVT\A,B SWAP) HSE,即硬件安全引擎(Hardware Security Engine),是NXP S32K312微控制器(MCU)中的一个功能,它提供了一系列的安全特性,包括加密、安全启动和密钥存储等。这些功能旨在增强汽车电子系统的信息安全,符合ISO26262标准,达到ASIL D安全等级。具体来说,S32K312的HSE支持AES-128/192/256加密、RSA、ECC等加密算法,以及侧通道保护,确保了数据传输和存储的安全性。 阅读详情

相关推荐

相机中的归一化实现讲解

本分讲解了图像处理中常出现的两个相机归一化操作

weixin_42102084的博客 1474

高并发之volatile、synchronized关键和内存屏障(Memory Barrier)

我们都知道在Java中,如果不使用volatile和synchronized指令可能会发生重排,指令重排分为编译器指令重排和CPU指令重排。 - Java多线程程序通常使用高层程序设计语言中的同步原语,比如volatile和synchronized,因此一般不需要明确使用内存屏障。 - 也就是说在Java中我们使用的是volatile和synchronized关键字,javac编译转化成字节码的时候,还是用到了内存屏障。

MrYushiwen的博客 2681

【DIY】使用STM32及PID算法实现一个磁悬浮玩具

今天教大家来做一个磁悬浮玩具。

小白白学电子 1万+

揭开内存屏障的面纱

推荐阅读(强烈推荐)c++标准库内存屏障的使用 一 什么是内存屏障 内存屏障(英语:Memory barrier),也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。 每个CPU都会有自己的缓存(有的甚至L1,L2,L3),缓存的目的就是为了提高性...

D_Guco的专栏 3892

GIS小白也能看懂的实战:5步教你将ArcGIS里的等高线和水系完美导入CAD做规划图

本文详细介绍了如何将ArcGIS中的等高线和水系数据高效导入CAD,制作专业规划底图的5个关键步骤。从数据准备、ArcGIS处理到CAD优化,涵盖坐标系设置、数据裁剪、格式转换等实用技巧,特别适合设计规划领域的新手快速掌握GIS与CAD协同工作流程。

weixin_27966471的博客 425

【2024遥感应用组一等奖】基于风云四号卫星的小时级大气气溶胶和颗粒物监测

最后,比较AGRI观测反射率和查找表计算的反射率反演得到的1小时分辨率AOD数据。然后,使用广泛应用的MODIS DT和DB产品进行对比,图 5显示了它们逐像元尺度的对比,AGRI LaGA算法可以捕捉亚洲地区AOD高低值空间分布格局,比如南亚次大陆、中南半岛、塔克拉玛干沙漠和华北平原是AOD高值区域(平均AOD高于0.3),中国西北、中国南方、中国东北和韩国地区为AOD中值地区(平均AOD在0.2到0.3之间),北纬45度以上以及日本地区为AOD低值区域(平均AOD小于0.2),与全球平均AOD值接近。

JData_Engineer的博客 373

内存屏障简略介绍

内存屏障

qq_23350817的博客 558

手把手教你学Simulink--基于微电网的建模与仿真场景实例:独立运行模式下微电网的能量管理系统设计

本文详细介绍了使用Simulink对独立运行微电网能量管理系统进行建模与仿真的完整流程。主要包括:系统结构设计(分布式能源、储能系统、负荷等模块)、建模步骤(从创建项目到实现能量管理策略)、仿真设置与结果分析。通过建立基于规则的控制逻辑和优化调度算法,实现微电网的自主运行和能量优化管理。文章还提供了参数优化和多目标改进建议,为新能源和智能电网领域的研究与应用提供了实用指导。

MHD0815的博客 8581

理解 Memory barrier(内存屏障)

转自:http://name5566.com/4535.html 参考文献列表: http://en.wikipedia.org/wiki/Memory_barrier http://en.wikipedia.org/wiki/Out-of-order_execution https://www.kernel.org/doc/Documentation/memory-barriers.

上善若水 1万+

边缘AI工业落地:YOLO26-Seg + RK3588 NPU 极致部署(低功耗+实时实例分割,工业场景全覆盖)

本次实战汇总了「YOLO26-Seg+RK3588 NPU」工业边缘部署中最常见、最致命的8个坑点,所有坑点均来自真实工业项目,解决方案均为零代码/一键操作,无需复杂配置,能让你少走90%的弯路,节省大量时间,必看必记!❌ RKNN转换报错「算子不支持/模型加载失败」→ ✅ 导出ONNX时必须指定opset=12,重新导出即可,RK3588唯一兼容版本;❌ 分割mask错位/轮廓模糊 → ✅ 导出ONNX时加,并保证RKNN的归一化参数与YOLO26一致,零代码修复;

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1717

Amazon SES 移出沙盒完整指南 & 高通过率模板

AWS SES沙盒模式转正式模式申请指南 申请正式模式需通过AWS审核,常见被拒原因包括邮件用途不清晰、缺少退订机制或发送量不合理。人工申诉需登录AWS Support Center提交服务限额提升请求,填写详细的公司信息、邮件用途及合规措施。 高通过率申诉模板需包含: 公司信息:名称、网址及与AWS的合作情况。 邮件规范:明确类型(交易/营销)、退订链接及退信处理机制。 发送计划:合理预估发送量,确保合规。 关键点:真实信息、用途清晰、退订机制完善、发送量合理。首次被拒可修改后重新提交。

xianyinsuifeng的博客 328

2020-04-27 三种内存屏障 acquire barrier / release barrier / full barrier

原文: http://www.yebangyu.org/blog/2015/10/31/linux-parallen-programmming-infrastructure/ 摘抄 Memory Barrier就是用来防止乱序执行的。具体说来,Memory Barrier包括三种: acquire barrier。acquire barrier之后的指令不能也不会被拉到该acquire ...

阿群的笔记(同步备份自简书) 1624

如何在 Mac 上下载安装 Xcode——并为 iOS 开发更新它原文

下载 Xcode安装命令行工具打开新版本删除文件注意,我在下面的步骤中列出了一些终端命令。这些命令可以被输入到你现在的工作目录中。这意味着你不需要导航到任何特定的文件夹。如果你真的想这样做,你可以在输入下面的步骤中的命令之前先输入== cd==。这将使你返回到主文件夹。

专注于AI领域前沿知识,业余爱好开发软件 4666

内存栅栏和volatile关键字

内存栅栏和volatile关键字   前言   本次主要讲解关于内存栅栏的一点小东西,主要是扫盲,给大家普及普及概念性的东西.以前我们说过在一些简单的案例中,比如一个字段赋值或递增该字段,我们需要对线程进行同步.   虽然lock可以满足我们的需要,但是一个竞争锁一定会导致阻塞,然后忍受线程上下文切换和调度的开销.有些高并发和性能比较关键的地方,这些

见证大牛成长之路的专栏 7005

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

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

关注了就能看到更多这么棒的文章哦~Lockless patterns: some final topicsMarch 29, 2021This article was contribute...

Linux News搬运工 592

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Volatile 详解

认识Volatile 首先,java中地Volatile可以实现可见性。 例如,以下代码, package pop.thread.demo3; /** * @author Pop * @date 2019/6/18 22:48 */ public class VolatileDemo { public volatile static boolean volatile_stop ...

CandyCCCation的博客 391

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Linux内核链表 内存屏障,为什么需要内存屏障

Author:Echo Chen(陈斌)Email:chenb19870707@gmail.comDate:September 30th, 2014来自一篇墙外的文章,要了解如何使用memory barrier,最好的方法是明白它为什么存在。CPU硬件设计为了提高指令的执行速度,增设了两个缓冲区(store buffer, invalidate queue)。这个两个缓冲区可以避免CPU在某些情况...

weixin_34364239的博客 241

Volatile最终解释

java中volatile的最终解释,从HotSpot源码到CPU缓存StoreBuffer

weixin_56892092的博客 1545

LWN:介绍lockless算法!

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

Linux News搬运工 1319

Linux kernel design patterns - part 1

One of the topics of ongoing interest in the kernel community is that of maintaining quality. It is trivially obvious that we need to maintain and ...

chijiandao3197的博客 416

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

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

Linux News搬运工 864

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

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

Linux News搬运工 664

linux内核分析之内存管理

1、struct page 1 /* Each physical page in the system has a struct page associated with 2 * it to keep track of whatever it is we are using the page for at the 3 * moment. Note that we have n...

weixin_34408717的博客 202

Benchmarks of the Lockless Memory Allocator

Benchmarks of the Lockless Memory Allocator To show how fast the Lockless memory allocator is compared to others, we use the t-test1 benchmark. This benchmark is given a total amount of memory to u

骆驼的专栏 994
上一篇: LWN: Linux 5.12中非常非常不妙的错误!
下一篇: LWN:BPF与io_uring的结合!
LinuxNews搬运工
博客等级 码龄7年 438粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值