首页 > 其他分享 >本地原子操作的语义和行为 【ChatGPT】

本地原子操作的语义和行为 【ChatGPT】

时间:2023-12-09 20:13:38浏览次数:39  
标签:语义 timer cpu 计数器 本地 test ChatGPT local CPU

这篇文档介绍了本地原子操作的语义和行为,以及如何在任何给定的架构中实现它们,并展示了它们如何被正确地使用。它还强调了在读取这些本地变量时必须采取的预防措施,特别是当内存写入的顺序很重要时。

注意
请注意,不建议在一般内核使用中使用基于 local_t 的操作。除非确实有特殊目的,请改用 this_cpu 操作。内核中大多数对 local_t 的使用已被 this_cpu 操作所取代。this_cpu 操作将重定位与 local_t 类似的语义结合在单个指令中,生成更紧凑和执行速度更快的代码。

本地原子操作的目的

本地原子操作旨在提供快速和高度可重入的每 CPU 计数器。它通过消除通常需要同步跨 CPU 的 LOCK 前缀和内存屏障来最小化标准原子操作的性能成本。

在许多情况下,拥有快速的每 CPU 原子计数器是很有意义的:它不需要禁用中断来保护中断处理程序,并且允许 NMI 处理程序中的一致计数器。这对于跟踪目的和各种性能监视计数器尤其有用。

本地原子操作仅保证相对于拥有数据的 CPU 的变量修改的原子性。因此,必须小心确保只有一个 CPU 写入 local_t 数据。这是通过使用每 CPU 数据并确保我们从一个抢占安全的上下文中修改它来完成的。但是,允许从任何 CPU 读取 local_t 数据:然后它将似乎相对于拥有者 CPU 的其他内存写入是无序的。

针对特定架构的实现

可以通过轻微修改标准原子操作来实现它:只需保留它们的 UP 变体。这通常意味着删除 LOCK 前缀(在 i386 和 x86_64 上)和任何 SMP 同步屏障。如果架构在 SMP 和 UP 之间没有不同的行为,则在您的架构的 local.h 中包含 asm-generic/local.h 就足够了。

local_t 类型被定义为一个不透明的有符号长整型,通过在结构体中嵌入 atomic_long_t 来实现。这样做是为了使得从这种类型到长整型的转换失败。定义如下:

typedef struct { atomic_long_t a; } local_t;

在使用本地原子操作时需要遵循的规则

  • 被本地操作触及的变量必须是每 CPU 变量。
  • 只有这些变量的 CPU 拥有者才能对其进行写入。
  • 这个 CPU 可以在任何上下文(进程、中断、软中断、NMI 等)中使用本地操作来更新它的 local_t 变量。
  • 在进程上下文中使用本地操作时必须禁用抢占(或中断),以确保进程在获取每 CPU 变量并执行实际的本地操作之间不会被迁移到不同的 CPU。
  • 在中断上下文中使用本地操作时,在主线内核上不需要特别注意,因为它们将在已经禁用抢占的本地 CPU 上运行。然而,我建议无论如何明确禁用抢占,以确保它在 -rt 内核上仍能正确工作。
  • 读取本地 CPU 变量将提供变量的当前副本。
  • 可以从任何 CPU 读取这些变量,因为对于“长整型”对齐的变量的更新始终是原子的。由于写入 CPU 没有进行内存同步,因此在读取其他 CPU 的变量时可能会读取到变量的过时副本。

如何使用本地原子操作

#include <linux/percpu.h>
#include <asm/local.h>

static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);

计数

对有符号长整型的所有位进行计数。

在可抢占的上下文中,使用 get_cpu_var() 和 put_cpu_var() 包围本地原子操作:它确保在写访问每 CPU 变量时禁用了抢占。例如:

local_inc(&get_cpu_var(counters));
put_cpu_var(counters);

如果您已经在一个抢占安全的上下文中,可以使用 this_cpu_ptr() 代替:

local_inc(this_cpu_ptr(&counters));

读取计数器

可以从其他 CPU 读取这些本地计数器以对计数进行求和。请注意,跨 CPU 的 local_read 看到的数据必须被认为相对于拥有数据的 CPU 上发生的其他内存写入是无序的:

long sum = 0;
for_each_online_cpu(cpu)
        sum += local_read(&per_cpu(counters, cpu));

如果要使用远程 local_read 在 CPU 之间同步对资源的访问,必须在写入 CPU 和读取 CPU 上分别使用显式的 smp_wmb() 和 smp_rmb() 内存屏障。如果您将 local_t 变量用作缓冲区中写入的字节数的计数器,那么在缓冲区写入和计数器增加之间应该有一个 smp_wmb(),并且在计数器读取和缓冲区读取之间应该有一个 smp_rmb()。

以下是一个使用 local.h 实现基本每 CPU 计数器的示例模块:

/* test-local.c
 *
 * 用于 local.h 使用的示例模块。
 */

#include <asm/local.h>
#include <linux/module.h>
#include <linux/timer.h>

static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);

static struct timer_list test_timer;

/* 在每个 CPU 上调用的 IPI。 */
static void test_each(void *info)
{
        /* 从非抢占上下文中增加计数器 */
        printk("在 CPU %d 上增加\n", smp_processor_id());
        local_inc(this_cpu_ptr(&counters));

        /* 在抢占安全的上下文中增加变量的操作如下(它禁用了抢占):
         *
         * local_inc(&get_cpu_var(counters));
         * put_cpu_var(counters);
         */
}

static void do_test_timer(unsigned long data)
{
        int cpu;

        /* 增加计数器 */
        on_each_cpu(test_each, NULL, 1);
        /* 读取所有计数器 */
        printk("从 CPU %d 读取计数器\n", smp_processor_id());
        for_each_online_cpu(cpu) {
                printk("读取:CPU %d,计数 %ld\n", cpu,
                        local_read(&per_cpu(counters, cpu)));
        }
        mod_timer(&test_timer, jiffies + 1000);
}

static int __init test_init(void)
{
        /* 初始化将增加计数器的定时器 */
        timer_setup(&test_timer, do_test_timer, 0);
        mod_timer(&test_timer, jiffies + 1);

        return 0;
}

static void __exit test_exit(void)
{
        timer_shutdown_sync(&test_timer);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mathieu Desnoyers");
MODULE_DESCRIPTION("本地原子操作");

标签:语义,timer,cpu,计数器,本地,test,ChatGPT,local,CPU
From: https://www.cnblogs.com/pengdonglin137/p/17891394.html

相关文章

  • 为内核对象添加引用计数器(krefs)【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/kref.html为内核对象添加引用计数器(krefs)作者[email protected]作者[email protected]其中很多内容都是从GregKroah-Hartman的2004年OLS论文和关于krefs的演示中借鉴而来的,可以在......
  • Linux kernel memory barriers 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/wrappers/memory-barriers.htmlLinux内核内存屏障免责声明本文档不是一个规范;它故意(为了简洁)和无意(因为是人类)不完整。本文档旨在指导如何使用Linux提供的各种内存屏障,但如果有任何疑问(而且有很多),请咨询。一些疑问可能通过参......
  • refcount_t API 与 atomic_t 的比较 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/refcount-vs-atomic.htmlrefcount_tAPI与atomic_t的比较介绍相关的内存排序类型函数比较非“读/修改/写”(RMW)操作基于增量的操作,不返回值基于减量的RMW操作,不返回值基于增量的RMW操作,返回值通用的减......
  • OneDrive如何保留云端文件删除本地文件
    OneDrive如何保留云端文件删除本地文件onedrive使用方法 > 常见问题 > OneDrive如何保留云端文件删除本地文件最近使用oneindex+ondriver搭建了一个个人的私人网盘,数据从本地电脑上传到onedriver网盘中,但是现在发现本地的文件越来越多,让电脑内存越来越少了。但是想想......
  • 中断 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/irq/index.htmlIRQs什么是IRQ?SMPIRQ亲和性Linux内核中的irq_domain中断号映射库IRQ标志状态跟踪什么是IRQ?IRQ(中断请求)是设备发出的中断请求。目前,它们可以通过引脚或数据包传输而来。多个设备可以连接到同一个引脚上......
  • CPU热插拔在内核中的支持 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/cpu_hotplug.htmlCPU热插拔在内核中的支持日期2021年9月作者[email protected],[email protected],[email protected],[email protected]......
  • Linux下的Cache和TLB刷新 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/cachetlb.htmlLinux下的Cache和TLB刷新作者:[email protected]本文描述了LinuxVM子系统调用的缓存/TLB刷新接口。它枚举了每个接口,描述了其预期目的以及在调用接口后预期的副作用。下面描述的副作用是针对单......
  • 在FS/IO上下文使用的GFP掩码 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/gfp_mask-from-fs-io.htmlGFPmasksusedfromFS/IOcontext日期2018年5月作者[email protected]简介文件系统和IO堆栈中的代码路径在分配内存时必须小心,以防止直接内存回收调用回FS或IO路径并在已持有的资......
  • pin_user_pages()及相关调用 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/pin_user_pages.htmlpin_user_pages()及相关调用概述本文档描述以下函数:pin_user_pages()pin_user_pages_fast()pin_user_pages_remote()FOLL_PIN的基本描述FOLL_PIN和FOLL_LONGTERM是可以传递给get_user_pages*()("gup......
  • 内存热插拔 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/memory-hotplug.html内存热插拔内存热插拔事件通知热插拔事件被发送到一个通知队列中。在include/linux/memory.h中定义了六种通知类型:MEM_GOING_ONLINE在新内存可用之前生成,以便准备子系统处理内存。页分配器仍然无法......