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

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

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

注意

请注意,不建议在一般内核使用中使用基于 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("本地原子操作");

本文来自博客园,作者:摩斯电码,未经同意,禁止转载

合集: 翻译2

标签: 翻译

好文要顶 关注我 收藏该文 微信分享

摩斯电码

粉丝 - 334 关注 - 54

+加关注

0

0

升级成为会员

« 上一篇: refcount_t API 与 atomic_t 的比较 【ChatGPT】

» 下一篇: 为内核对象添加引用计数器(krefs)【ChatGPT】

posted @ 2023-12-09 19:56  摩斯电码  阅读(6)  评论(0)  编辑  收藏  举报

刷新评论刷新页面返回顶部

发表评论 升级成为园子VIP会员

编辑预览

自动补全

退出 订阅评论 我的博客

[Ctrl+Enter快捷键提交]

【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步

【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!

【推荐】会员力量,点亮园子希望,期待您升级成为博客园VIP会员

相关文章
|
6月前
|
机器学习/深度学习 自然语言处理 搜索推荐
如何避免LLM的“幻觉”(Hallucination)
生成式大语言模型(LLM)可以针对各种用户的 prompt 生成高度流畅的回复。然而,大模型倾向于产生幻觉或做出非事实陈述,这可能会损害用户的信任。
113 1
|
自然语言处理
chatGPT意义空间和语义运动定律
chatGPT(Chat Generative Pre-trained Transformer)是一种基于Transformer模型的语言生成模型,具有强大的语言理解和生成能力。它在自然语言处理领域具有广泛的应用,可以用于对话系统、问答系统、翻译系统等任务。 在chatGPT的预训练过程中,模型通过大规模的对话数据进行训练,从而学习到了丰富的语言知识和上下文关联性。这使得chatGPT能够生成连贯、流畅的回复,并且能够理解人类的语言表达。 在预训练过程中,chatGPT使用的是基于自回归的语言模型。这意味着模型在生成每个单词时都会考虑前面生成的单词,从而保证生成的文本具有一定的连贯性和上下
70 0
|
1月前
|
机器学习/深度学习 存储 自然语言处理
NLP 面试揭秘:解锁 注意力机制
NLP 面试揭秘:解锁 注意力机制
104 65
NLP 面试揭秘:解锁 注意力机制
|
6月前
|
机器学习/深度学习 数据可视化
如何解决大语言模型的幻觉问题
如何解决大语言模型的幻觉问题
|
3月前
|
机器学习/深度学习
Sora 原理使用问题之Sora提示词的语义并进行视频生成该如何理解
Sora 原理使用问题之Sora提示词的语义并进行视频生成该如何理解
|
3月前
|
机器学习/深度学习 自然语言处理
ChatGPT 等相关大模型问题之Attention 机制的定义如何解决
ChatGPT 等相关大模型问题之Attention 机制的定义如何解决
|
3月前
|
机器学习/深度学习 人工智能 算法
ChatGPT 等相关大模型问题之ChatGPT 的概念如何解决
ChatGPT 等相关大模型问题之ChatGPT 的概念如何解决
|
5月前
|
机器学习/深度学习 自然语言处理 PyTorch
多模态条件机制
多模态条件机制
115 0
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
机器翻译语义层面
机器翻译语义层面
75 1
|
Java
知识单元九 多线程
知识单元九 多线程
106 0