前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux互斥与同步之原子操作

Linux互斥与同步之原子操作

作者头像
DragonKingZhu
发布2022-05-08 16:47:20
1K0
发布2022-05-08 16:47:20
举报

例子

一个全局共享的变量flag

代码语言:javascript
复制
int flag=0

进程A

代码语言:javascript
复制
void funcA()
{
    flag++;
}

进程B

代码语言:javascript
复制
void funcB()
{
    flag++
}

在进程A和进程B都运行起来后,flag的值应该会是多少?

case1

funcA先执行,再执行funcB。或者 funcB先执行,再执行funcA。 上述无论那个先执行,结果都是2。没有什么多说的。

case2

先说下flag++在汇编级别的代码:

代码语言:javascript
复制
P1. mov %eax $flag   //将flag保存到寄存器中
P2. inc %eax         //将寄存器中的值加1
P3. mov $flag %eax   //将寄存器中的值回写到flag中

如果funcA先执行,当执行完P1指令后,没有执行P2指令时。funcB被处理器调度执行,当funcB从头到尾执行完后,flag等于1。然后回到funcA打断的节点上,继续执行。因为以前执行了P1,导致eax中的值依然是0,所以funcA执行完后,flag还是为1。

对于单CPU当执行系统调用的时候有中断发生,也会出现上述情况。 对于多CPU,如果不是percpu变量,多个CPU共享的,也会出现多个CPU修改同一个值的现象。

针对上述情况,kernel就针对保护一个整型变量提出了原子变量。

原子变量

Linux源码中定义了一个类型为atomic_t的原子变量。

代码语言:javascript
复制
typedef struct{
    int counter;
}atomic_t;

#define CONFIG_64BIT
typedef struct{
    long counter;
}atomic64_t;
#endif

从上述定义可以看出,在32位上atomic_t就是一个init型counter, 在64位系统上使用的是long型变量。

原子操作函数集

接口函数

详细说明

atomic_add(int i, atomic_t *v)

给原子变量v加i

atomic_sub(int i, atomic_t *v)

给原子变量v减i

atomic_inc(atomic_t *v)

原子变量v加1

atomic_dec(atomic_t *v)

原子变量v减1

atomic_add_return(int i, atomic_t *v)

给原子变量v加i,并将最新的v返回

atomic_sub_return(int i, atomic_t *v)

给原子变量v减i,并将最新的v返回

atomic_read(v)

获得原子变量的值

atomic_set(v, i)

给原子变量v设置为i

atomic_cmpxchg(v, old, new)

比较old和v的值是否相等,如果相等,就把new赋值给v

__atomic_add_unless(v, a, u)

如果u不等与c,就将v+a复制给v

以上是atomic_t绝大多数的原子操作函数集合。这些函数实现都依赖于特定的硬件平台。

x86上atomic_add实现

代码语言:javascript
复制
 /**
  * atomic_add - add integer to atomic variable
  * @i: integer value to add
  * @v: pointer of type atomic_t
  *
  * Atomically adds @i to @v.
  */
 static inline void atomic_add(int i, atomic_t *v)
 {
     asm volatile(LOCK_PREFIX "addl %1,%0"
              : "+m" (v->counter)
              : "ir" (i));
 }

x86上用带有“LOCK_PREFIX前缀的addl指令来保证原子变量v加1操作的原子性。”LOCK_PREFIX“前缀在x86上的作用在执行add指令时独占系统总线。这样在执行期间,别的进程也无法修改counter的值

ARM上atomic_add_resturn实现

代码语言:javascript
复制
static inline int atomic_add_return(int i, atomic_t *v)      \
{                                   \
     unsigned long tmp;                      \
     int result;                         \
                                    \
     asm volatile("// atomic_add_return\n"           \
 "1: ldxr    %w0, %2\n"                      \
 "   add     %w0, %w0, %w3\n"                \
 "   stlxr   %w1, %w0, %2\n"                     \
 "   cbnz    %w1, 1b"                        \
     : "=&r" (result), "=&r" (tmp), "+Q" (v->counter)        \
     : "Ir" (i)                          \
     : "memory");                            \
                                     \
     smp_mb();                           \
     return result;                          \
 }

汇编语言

C语言

1: ldxr %w0, %2

result = v->counter;

add %w0, %w0, %w3

result = result + i;

stlxr %w1, %w0, %2

v->counter = result; tmp = 设置是否成功;

cnnz %w1, 1b

if(tmp != 0) goto 1;

再看开始的例子

一个全局共享的变量flag

代码语言:javascript
复制
atomic_t flag=ATOMIC_INIT(0);

进程A

代码语言:javascript
复制
void funcA()
{
    atomic_inc(&flag);
}

进程B

代码语言:javascript
复制
void funcB()
{
    atomic_inc(&flag);
}

这样之后,无论A或者B谁先执行,结果都是2。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 例子
  • case1
  • case2
  • 原子变量
  • 原子操作函数集
  • x86上atomic_add实现
  • ARM上atomic_add_resturn实现
  • 再看开始的例子
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档