在任何处理器平台下,都会有一些原子性操作,供操作系统使用,我们这里只讲x86下面的。在单处理器情况下,每条指令的执行都是原子性的,但在多处理器情况下,只有那些单独的读操作或写操作才是原子性的。为了弥补这一缺点,x86提供了附加的lock前缀,使带lock前缀的读修改写指令也能原子性执行。带lock前缀的指令在操作时会锁住总线,使自身的执行即使在多处理器间也是原子性执行的。xchg指令不带lock前缀也是原子性执行,也就是说xchg执行时默认会锁内存总线。原子性操作是线程间同步的基础,linux专门定义了一种只进行原子操作的类型atomic_t,并提供相关的原子读写调用API。本节就来分析这些原子操作在x86下的实现。

  1. typedef struct {
  2. volatile int counter;
  3. } atomic_t;

原子类型其实是int类型,只是禁止寄存器对其暂存。

  1. #define ATOMIC_INIT(i)  { (i) }

原子类型的初始化。32位x86平台下atomic API在arch/x86/include/asm/atomic_32.h中实现。

  1. static inline int atomic_read(const atomic_t *v)
  2. {
  3. return v->counter;
  4. }
  5. static inline void atomic_set(atomic_t *v, int i)
  6. {
  7. v->counter = i;
  8. }

单独的读操作或者写操作,在x86下都是原子性的。

  1. static inline void atomic_add(int i, atomic_t *v)
  2. {
  3. asm volatile(LOCK_PREFIX "addl %1,%0"
  4. : "+m" (v->counter)
  5. : "ir" (i));
  6. }
  7. static inline void atomic_sub(int i, atomic_t *v)
  8. {
  9. asm volatile(LOCK_PREFIX "subl %1,%0"
  10. : "+m" (v->counter)
  11. : "ir" (i));
  12. }

atomic_add和atomic_sub属于读修改写操作,实现时需要加lock前缀。

  1. static inline int atomic_sub_and_test(int i, atomic_t *v)
  2. {
  3. unsigned char c;
  4. asm volatile(LOCK_PREFIX "subl %2,%0; sete %1"
  5. : "+m" (v->counter), "=qm" (c)
  6. : "ir" (i) : "memory");
  7. return c;
  8. }

atomic_sub_and_test执行完减操作后检查结果是否为0。

  1. static inline void atomic_inc(atomic_t *v)
  2. {
  3. asm volatile(LOCK_PREFIX "incl %0"
  4. : "+m" (v->counter));
  5. }
  6. static inline void atomic_dec(atomic_t *v)
  7. {
  8. asm volatile(LOCK_PREFIX "decl %0"
  9. : "+m" (v->counter));
  10. }

atomic_inc和atomic_dec是递增递减操作。

  1. static inline int atomic_dec_and_test(atomic_t *v)
  2. {
  3. unsigned char c;
  4. asm volatile(LOCK_PREFIX "decl %0; sete %1"
  5. : "+m" (v->counter), "=qm" (c)
  6. : : "memory");
  7. return c != 0;
  8. }

atomic_dec_and_test在递减后检查结果是否为0。

  1. static inline int atomic_inc_and_test(atomic_t *v)
  2. {
  3. unsigned char c;
  4. asm volatile(LOCK_PREFIX "incl %0; sete %1"
  5. : "+m" (v->counter), "=qm" (c)
  6. : : "memory");
  7. return c != 0;
  8. }

atomic_inc_and_test在递增后检查结果是否为0。

  1. static inline int atomic_add_negative(int i, atomic_t *v)
  2. {
  3. unsigned char c;
  4. asm volatile(LOCK_PREFIX "addl %2,%0; sets %1"
  5. : "+m" (v->counter), "=qm" (c)
  6. : "ir" (i) : "memory");
  7. return c;
  8. }

atomic_add_negative在加操作后检查结果是否为负数。

  1. static inline int atomic_add_return(int i, atomic_t *v)
  2. {
  3. int __i;
  4. #ifdef CONFIG_M386
  5. unsigned long flags;
  6. if (unlikely(boot_cpu_data.x86 <= 3))
  7. goto no_xadd;
  8. #endif
  9. /* Modern 486+ processor */
  10. __i = i;
  11. asm volatile(LOCK_PREFIX "xaddl %0, %1"
  12. : "+r" (i), "+m" (v->counter)
  13. : : "memory");
  14. return i + __i;
  15. #ifdef CONFIG_M386
  16. no_xadd: /* Legacy 386 processor */
  17. local_irq_save(flags);
  18. __i = atomic_read(v);
  19. atomic_set(v, i + __i);
  20. local_irq_restore(flags);
  21. return i + __i;
  22. #endif
  23. }

atomic_add_return 不仅执行加操作,而且把相加的结果返回。它是通过xadd这一指令实现的。

  1. static inline int atomic_sub_return(int i, atomic_t *v)
  2. {
  3. return atomic_add_return(-i, v);
  4. }

atomic_sub_return 不仅执行减操作,而且把相减的结果返回。它是通过atomic_add_return实现的。

  1. static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
  2. {
  3. return cmpxchg(&v->counter, old, new);
  4. }
  5. #define cmpxchg(ptr, o, n)                      \
  6. ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o),   \
  7. (unsigned long)(n),      \
  8. sizeof(*(ptr))))
  9. static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
  10. unsigned long newint size)
  11. {
  12. unsigned long prev;
  13. switch (size) {
  14. case 1:
  15. asm volatile(LOCK_PREFIX "cmpxchgb %b1,%2"
  16. : "=a"(prev)
  17. : "q"(new), "m"(*__xg(ptr)), "0"(old)
  18. : "memory");
  19. return prev;
  20. case 2:
  21. asm volatile(LOCK_PREFIX "cmpxchgw %w1,%2"
  22. : "=a"(prev)
  23. : "r"(new), "m"(*__xg(ptr)), "0"(old)
  24. : "memory");
  25. return prev;
  26. case 4:
  27. asm volatile(LOCK_PREFIX "cmpxchgl %k1,%2"
  28. : "=a"(prev)
  29. : "r"(new), "m"(*__xg(ptr)), "0"(old)
  30. : "memory");
  31. return prev;
  32. case 8:
  33. asm volatile(LOCK_PREFIX "cmpxchgq %1,%2"
  34. : "=a"(prev)
  35. : "r"(new), "m"(*__xg(ptr)), "0"(old)
  36. : "memory");
  37. return prev;
  38. }
  39. return old;
  40. }

atomic_cmpxchg是由cmpxchg指令完成的。它把旧值同atomic_t类型的值相比较,如果相同,就把新值存入atomic_t类型的值中,返回atomic_t类型变量中原有的值。

  1. static inline int atomic_xchg(atomic_t *v, int new)
  2. {
  3. return xchg(&v->counter, new);
  4. }
  5. #define xchg(ptr, v)                            \
  6. ((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), sizeof(*(ptr))))
  7. static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
  8. int size)
  9. {
  10. switch (size) {
  11. case 1:
  12. asm volatile("xchgb %b0,%1"
  13. : "=q" (x)
  14. : "m" (*__xg(ptr)), "0" (x)
  15. : "memory");
  16. break;
  17. case 2:
  18. asm volatile("xchgw %w0,%1"
  19. : "=r" (x)
  20. : "m" (*__xg(ptr)), "0" (x)
  21. : "memory");
  22. break;
  23. case 4:
  24. asm volatile("xchgl %k0,%1"
  25. : "=r" (x)
  26. : "m" (*__xg(ptr)), "0" (x)
  27. : "memory");
  28. break;
  29. case 8:
  30. asm volatile("xchgq %0,%1"
  31. : "=r" (x)
  32. : "m" (*__xg(ptr)), "0" (x)
  33. : "memory");
  34. break;
  35. }
  36. return x;
  37. }

atomic_xchg则是将新值存入atomic_t类型的变量,并将变量的旧值返回。它使用xchg指令实现。

  1. /**
  2. * atomic_add_unless - add unless the number is already a given value
  3. * @v: pointer of type atomic_t
  4. * @a: the amount to add to v...
  5. * @u: ...unless v is equal to u.
  6. *
  7. * Atomically adds @a to @v, so long as @v was not already @u.
  8. * Returns non-zero if @v was not @u, and zero otherwise.
  9. */
  10. static inline int atomic_add_unless(atomic_t *v, int a, int u)
  11. {
  12. int c, old;
  13. c = atomic_read(v);
  14. for (;;) {
  15. if (unlikely(c == (u)))
  16. break;
  17. old = atomic_cmpxchg((v), c, c + (a));
  18. if (likely(old == c))
  19. break;
  20. c = old;
  21. }
  22. return c != (u);
  23. }

atomic_add_unless的功能比较特殊。它检查v是否等于u,如果不是则把v的值加上a,返回值表示相加前v是否等于u。因为在atomic_read和atomic_cmpxchg中间可能有其它的写操作,所以要循环检查自己的值是否被写进去。

  1. #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
  2. #define atomic_inc_return(v)  (atomic_add_return(1, v))
  3. #define atomic_dec_return(v)  (atomic_sub_return(1, v))

atomic_inc_not_zero在v值不是0时加1。

atomic_inc_return对v值加1,并返回相加结果。

atomic_dec_return对v值减1,并返回相减结果。

  1. #define atomic_clear_mask(mask, addr)               \
  2. asm volatile(LOCK_PREFIX "andl %0,%1"           \
  3. : : "r" (~(mask)), "m" (*(addr)) : "memory")

atomic_clear_mask清除变量某些位。

  1. #define atomic_set_mask(mask, addr)             \
  2. asm volatile(LOCK_PREFIX "orl %0,%1"                \
  3. : : "r" (mask), "m" (*(addr)) : "memory")

atomic_set_mask将变量的某些位置位。

  1. /* Atomic operations are already serializing on x86 */
  2. #define smp_mb__before_atomic_dec() barrier()
  3. #define smp_mb__after_atomic_dec()  barrier()
  4. #define smp_mb__before_atomic_inc() barrier()
  5. #define smp_mb__after_atomic_inc()  barrier()

因为x86的atomic操作大多使用原子指令或者带lock前缀的指令。带lock前缀的指令执行前会完成之前的读写操作,对于原子操作来说不会受之前对同一位置的读写操作,所以这里只是用空操作barrier()代替。barrier()的作用相当于告诉编译器这里有一个内存屏障,放弃在寄存器中的暂存值,重新从内存中读入。

本节的atomic_t类型操作是最基础的,为了介绍下面的内容,必须先介绍它。如果可以使用atomic_t类型代替临界区操作,也可以加快不少速度。

Linux内核部件分析 原子性操作atomic_t的更多相关文章

  1. 24小时学通Linux内核之如何处理输入输出操作

    真的是悲喜交加呀,本来这个寒假早上8点都去练车,两个小时之后再来实验室陪伴Linux内核,但是今天教练说没名额考试了,好纠结,不过想想就可以睡懒觉了,哈哈,自从大三寒假以来还没睡过懒觉呢,现在也有更多 ...

  2. linux内核链表分析

    一.常用的链表和内核链表的区别 1.1  常规链表结构        通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...

  3. Linux内核源代码分析方法

    Linux内核源代码分析方法   一.内核源代码之我见 Linux内核代码的庞大令不少人"望而生畏",也正由于如此,使得人们对Linux的了解仅处于泛泛的层次.假设想透析Linux ...

  4. 2019-2020-1 20199303 《Linux内核原理分析》 第一周作业

    2019-2020-1 20199303 <Linux内核原理分析> 第一周作业 1. 环境准备 在众多的Linux发行版中,Ubuntu,小红帽还有类Unix系统的BSD系统,我选择了目 ...

  5. 从linux内核代码分析操作系统启动过程

    朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 在本次的实验中, ...

  6. Linux内核启动分析过程-《Linux内核分析》week3作业

    环境搭建 环境的搭建参考课件,主要就是编译内核源码和生成镜像 start_kernel 从start_kernel开始,才真正进入了Linux内核的启动过程.我们可以把start_kernel看做平时 ...

  7. Linux内核启动分析

    张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我的代码可见https://www.shiyanlo ...

  8. Linux内核及分析 第八周 进程的切换和系统的一般执行过程

    学习笔记: 一.进程调度与进程调度的时机分析 1.不同类型的进程有不同需求的调度需求: 第一种分类: —I/O-bound:频繁的进行I/O,通常会花费很多时间等待I/O操作的完成 —CPU-boun ...

  9. Linux内核及分析 第二周 操作系统是如何工作的?

    计算机是如何工作的? 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那么重要,但有了高级语言及函 ...

随机推荐

  1. MYSQL注入天书之基础知识

    第一部分/page-1 Basic Challenges Background-1 基础知识 此处介绍一些mysql注入的一些基础知识. (1)注入的分类---仁者见仁,智者见智. 下面这个是阿德玛表 ...

  2. xcoj 1103 插线板(树链刨分求最大子段和)

    1103: 插线板 时间限制: 1 Sec  内存限制: 128 MB提交: 14  解决: 7 标签提交统计讨论版EditTestData 题目描述 从前有一堆古老的插线板,任意两个插线板之间只有一 ...

  3. 洛谷 P3943 星空

    题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷. 你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小 F 看着夜空发呆. 天上空荡荡的,没有一颗星星——大概是因为天上 ...

  4. 【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7

    解题方法提示 小Hi:我们已经学习了后缀自动机,今天我们再来看这道有意思的题. 小Ho:好!这道题目让我们求的是若干的数字串所有不同子串的和. 小Hi:你能不能结合后缀自动机的性质来思考如何解决本题? ...

  5. 百度首席科学家 Andrew Ng谈深度学习的挑战和未来(转载)

    转载:http://www.csdn.net/article/2014-07-10/2820600 人工智能被认为是下一个互联网大事件,当下,谷歌.微软.百度等知名的高科技公司争相投入资源,占领深度学 ...

  6. WPF的UI虚拟化

    许多时候,我们的界面上会呈现大量的数据,如包含数千条记录的表格或包含数百张照片的相册.由于呈现UI是一件开销比较大的动作,一次性呈现数百张照片就目前的电脑性能来说是需要占用大量内存和时间的.因此需要对 ...

  7. 配置Node Manager启动同一机器的两个域

    进入/u01/FMW_Home/weblogic/wlserver_10.3/server/bin目录,在需要配置的节点上启动node manager,以weblogic用户登录 c d /u01/F ...

  8. BZOJ3157: 国王奇遇记 & 3516: 国王奇遇记加强版

    令\[S_i=\sum_{k=1}^n k^i m^k\]我们有\[\begin{eqnarray*}(m-1)S_i & = & mS_i - S_i \\& = & ...

  9. 【Todo】Boost安装与学习

    现在这里找下载包 http://sourceforge.net/projects/boost 我找的是 1_62_0 下面是从公司wiki上找到的一个说明. boost & thrift安装步 ...

  10. python开发者常犯的10个错误(转)

    常见错误1:错误地将表达式作为函数的默认参数 在Python中,我们可以为函数的某个参数设置默认值,使该参数成为可选参数.虽然这是一个很好的语言特性,但是当默认值是可变类型时,也会导致一些令人困惑的情 ...