~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/Documentation/translations/zh_CN/core-api/local_ops.rst

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 .. include:: ../disclaimer-zh_CN.rst
  2 
  3 :Original: Documentation/core-api/local_ops.rst
  4 
  5 :翻译:
  6 
  7  司延腾 Yanteng Si <siyanteng@loongson.cn>
  8 
  9 .. _cn_local_ops:
 10 
 11 ========================
 12 本地原子操作的语义和行为
 13 ========================
 14 
 15 :作者: Mathieu Desnoyers
 16 
 17 
 18 本文解释了本地原子操作的目的,如何为任何给定的架构实现这些操作,并说明了
 19 如何正确使用这些操作。它还强调了在内存写入顺序很重要的情况下,跨CPU读取
 20 这些本地变量时必须采取的预防措施。
 21 
 22 .. note::
 23 
 24     注意,基于 ``local_t`` 的操作不建议用于一般内核操作。请使用 ``this_cpu``
 25     操作来代替使用,除非真的有特殊目的。大多数内核中使用的 ``local_t`` 已
 26     经被 ``this_cpu`` 操作所取代。 ``this_cpu`` 操作在一条指令中结合了重
 27     定位和类似 ``local_t`` 的语义,产生了更紧凑和更快的执行代码。
 28 
 29 
 30 本地原子操作的目的
 31 ==================
 32 
 33 本地原子操作的目的是提供快速和高度可重入的每CPU计数器。它们通过移除LOCK前
 34 缀和通常需要在CPU间同步的内存屏障,将标准原子操作的性能成本降到最低。
 35 
 36 在许多情况下,拥有快速的每CPU原子计数器是很有吸引力的:它不需要禁用中断来保护中
 37 断处理程序,它允许在NMI(Non Maskable Interrupt)处理程序中使用连贯的计数器。
 38 它对追踪目的和各种性能监测计数器特别有用。
 39 
 40 本地原子操作只保证在拥有数据的CPU上的变量修改的原子性。因此,必须注意确保只
 41 有一个CPU写到 ``local_t`` 的数据。这是通过使用每CPU的数据来实现的,并确
 42 保我们在一个抢占式安全上下文中修改它。然而,从任何一个CPU读取 ``local_t``
 43 数据都是允许的:这样它就会显得与所有者CPU的其他内存写入顺序不一致。
 44 
 45 
 46 针对特定架构的实现
 47 ==================
 48 
 49 这可以通过稍微修改标准的原子操作来实现:只有它们的UP变体必须被保留。这通常
 50 意味着删除LOCK前缀(在i386和x86_64上)和任何SMP同步屏障。如果架构在SMP和
 51 UP之间没有不同的行为,在你的架构的 ``local.h`` 中包括 ``asm-generic/local.h``
 52 就足够了。
 53 
 54 通过在一个结构体中嵌入一个 ``atomic_long_t`` , ``local_t`` 类型被定义为
 55 一个不透明的 ``signed long`` 。这样做的目的是为了使从这个类型到
 56 ``long`` 的转换失败。该定义看起来像::
 57 
 58     typedef struct { atomic_long_t a; } local_t;
 59 
 60 
 61 使用本地原子操作时应遵循的规则
 62 ==============================
 63 
 64 * 被本地操作触及的变量必须是每cpu的变量。
 65 
 66 * *只有* 这些变量的CPU所有者才可以写入这些变量。
 67 
 68 * 这个CPU可以从任何上下文(进程、中断、软中断、nmi...)中使用本地操作来更新
 69   它的local_t变量。
 70 
 71 * 当在进程上下文中使用本地操作时,必须禁用抢占(或中断),以确保进程在获得每
 72   CPU变量和进行实际的本地操作之间不会被迁移到不同的CPU。
 73 
 74 * 当在中断上下文中使用本地操作时,在主线内核上不需要特别注意,因为它们将在局
 75   部CPU上运行,并且已经禁用了抢占。然而,我建议无论如何都要明确地禁用抢占,
 76   以确保它在-rt内核上仍能正确工作。
 77 
 78 * 读取本地cpu变量将提供该变量的当前拷贝。
 79 
 80 * 对这些变量的读取可以从任何CPU进行,因为对 “ ``long`` ”,对齐的变量的更新
 81   总是原子的。由于写入程序的CPU没有进行内存同步,所以在读取 *其他* cpu的变
 82   量时,可以读取该变量的过期副本。
 83 
 84 
 85 如何使用本地原子操作
 86 ====================
 87 
 88 ::
 89 
 90     #include <linux/percpu.h>
 91     #include <asm/local.h>
 92 
 93     static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);
 94 
 95 
 96 计数器
 97 ======
 98 
 99 计数是在一个signed long的所有位上进行的。
100 
101 在可抢占的上下文中,围绕本地原子操作使用 ``get_cpu_var()`` 和
102 ``put_cpu_var()`` :它确保在对每个cpu变量进行写访问时,抢占被禁用。比如
103 说::
104 
105     local_inc(&get_cpu_var(counters));
106     put_cpu_var(counters);
107 
108 如果你已经在一个抢占安全上下文中,你可以使用 ``this_cpu_ptr()`` 代替::
109 
110     local_inc(this_cpu_ptr(&counters));
111 
112 
113 
114 读取计数器
115 ==========
116 
117 那些本地计数器可以从外部的CPU中读取,以求得计数的总和。请注意,local_read
118 所看到的跨CPU的数据必须被认为是相对于拥有该数据的CPU上发生的其他内存写入来
119 说不符合顺序的::
120 
121     long sum = 0;
122     for_each_online_cpu(cpu)
123             sum += local_read(&per_cpu(counters, cpu));
124 
125 如果你想使用远程local_read来同步CPU之间对资源的访问,必须在写入者和读取者
126 的CPU上分别使用显式的 ``smp_wmb()`` 和 ``smp_rmb()`` 内存屏障。如果你使
127 用 ``local_t`` 变量作为写在缓冲区中的字节的计数器,就会出现这种情况:在缓
128 冲区写和计数器增量之间应该有一个 ``smp_wmb()`` ,在计数器读和缓冲区读之间
129 也应有一个 ``smp_rmb()`` 。
130 
131 下面是一个使用 ``local.h`` 实现每个cpu基本计数器的示例模块::
132 
133     /* test-local.c
134      *
135      * Sample module for local.h usage.
136      */
137 
138 
139     #include <asm/local.h>
140     #include <linux/module.h>
141     #include <linux/timer.h>
142 
143     static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0);
144 
145     static struct timer_list test_timer;
146 
147     /* IPI called on each CPU. */
148     static void test_each(void *info)
149     {
150             /* Increment the counter from a non preemptible context */
151             printk("Increment on cpu %d\n", smp_processor_id());
152             local_inc(this_cpu_ptr(&counters));
153 
154             /* This is what incrementing the variable would look like within a
155              * preemptible context (it disables preemption) :
156              *
157              * local_inc(&get_cpu_var(counters));
158              * put_cpu_var(counters);
159              */
160     }
161 
162     static void do_test_timer(unsigned long data)
163     {
164             int cpu;
165 
166             /* Increment the counters */
167             on_each_cpu(test_each, NULL, 1);
168             /* Read all the counters */
169             printk("Counters read from CPU %d\n", smp_processor_id());
170             for_each_online_cpu(cpu) {
171                     printk("Read : CPU %d, count %ld\n", cpu,
172                             local_read(&per_cpu(counters, cpu)));
173             }
174             mod_timer(&test_timer, jiffies + 1000);
175     }
176 
177     static int __init test_init(void)
178     {
179             /* initialize the timer that will increment the counter */
180             timer_setup(&test_timer, do_test_timer, 0);
181             mod_timer(&test_timer, jiffies + 1);
182 
183             return 0;
184     }
185 
186     static void __exit test_exit(void)
187     {
188             timer_shutdown_sync(&test_timer);
189     }
190 
191     module_init(test_init);
192     module_exit(test_exit);
193 
194     MODULE_LICENSE("GPL");
195     MODULE_AUTHOR("Mathieu Desnoyers");
196     MODULE_DESCRIPTION("Local Atomic Ops");

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php