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;
}

關於原子位操作

基本實現是和原子操作是一樣的,只是操作的是一位,所有速度會快。

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