01.Linux 内核同步与互斥-原子变量

1.内联汇编

# 内联汇编的语法
asm asm-qualifiers (AssemblerTemplate
                                :OutputOperands
                                [:InputOperands
                                [:Clobbers]])

int add(int a,int b)
{
    int sum;
    __asm__ volatile(
        "add %0, %1, %2"
        :"=r"(sum)
        :"r"(a),"r"(b)
        :"cc"
    );
    return sum;
}

ATPCS 的具体语法(https://baike.baidu.com/item/ATPCS/10695243?fr=aladdin)

  1. asm : 也可以写作 “asm”, 表示这是一段内联汇编
  2. asm-qualifiers 有 3 个取值:volatile、inline、goto。
    volatile 的意思是易变的、不稳定的,用来告诉编译器不要随便优化这段代码,否则可能出问题。比
    如汇编指令“mov r0, r0”,它把 r0 的值复制到 r0,并没有实际做什么事情,你的本意可能是用这条指令
    来延时。编译器看到这指令后,可能就把它去掉了。加上 volatile 的话,编译器就不会擅自优化。


  3. AssemblerTemplate
    汇编指令,用双引号包含起来,每条指令用“\n”分开
  4. OutputOperands
    输出操作数,内联汇编执行时,输出的结果保存在哪里。
    格式如下,当有多个变量时,用逗号隔开:
    [ [asmSymbolicName] ] constraint (cvariablename)


  5. InputOperands
    输入操作数,内联汇编执行前,输入的数据保存在哪里。
    格式如下,当有多个变量时,用逗号隔开:
    [ [asmSymbolicName] ] constraint (cexpression)


  6. Clobbers
    在汇编代码中,对于“OutputOperands”所涉及的寄存器、内存,肯定是做了修改。但是汇编代码中,
    也许要修改的寄存器、内存会更多。比如在计算过程中可能要用到 r3 保存临时结果,我们必须在“Clobbers”
    中声明 r3 会被修改


Clobbers 描述
cc 表示汇编代码会修改“flags register”
memory 表示汇编代码中,除了“InputOperands”和“OutputOperands”中指定的之外,还会会读、写更多的内存

2. atomic 实现

ATOMIC_OP 在 UP 系统中的实现,代码路径(arch\arm\include\asm\atomic.h)

对于 ARMv6 以下的 CPU 系统,不支持 SMP。原子变量的操作简单粗暴:关中断

01.Linux 内核同步与互斥-原子变量

ARMv6 及以上的 CPU,有一些特殊的汇编指令来实现原子操作,不再需要关中断

01.Linux 内核同步与互斥-原子变量

在 ARMv6 及以上的架构中,原子操作的执行过程是可以被打断的,但是它的效果符合“原子”的定义:
一个完整的“读、修改、写入”原子的,不会被别的程序打断。它的思路很简单:如果被别的程序打断了,
那就重来,最后总会成功的

3.原子变量对外的 API

类型定义 include\linux\types.h
01.Linux 内核同步与互斥-原子变量

函数名 作用
atomic_read(v) 读出原子变量的值,即 v->counter
atomic_set(v,i) 设置原子变量的值,即 v->counter = i
atomic_inc(v) v->counter++
atomic_dec(v) v->counter--
atomic_add(i,v) v->counter += i
atomic_sub(i,v) v->counter -= i
atomic_inc_and_test(v) 先加 1,再判断新值是否等于 0;等于 0 的话,返回值为 1
atomic_dec_and_test(v) 先减 1,再判断新值是否等于 0;等于 0 的话,返回值为 1

使用方法

static atomic_t valid = ATOMIC_INIT(1);
static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
{
    if (atomic_dec_and_test(&valid))
    {
        return 0;
    }
    atomic_inc(&valid);
    return -EBUSY;
}

static int gpio_key_drv_close (struct inode *node, struct file *file)
{
    atomic_inc(&valid);
    return 0;
}

关于原子位操作

基本实现是和原子操作是一样的,只是操作的是一位,所有速度会快。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章