1. 程式人生 > >linux驅動中的互斥途徑一二:中斷遮蔽和原子操作

linux驅動中的互斥途徑一二:中斷遮蔽和原子操作

1. 中斷遮蔽:

1.1 說明:

在單 CPU 範圍內避免競態的簡單而省事的方法是在進入臨界區之前遮蔽系統的中斷 優點是:當中斷遮蔽的時候,核心搶佔程序之間的併發也得以避免了缺點是:由於linux的非同步I/O、程序排程等很多重要操作都依賴於中斷,這些功能在中斷遮蔽期間將不能使用。當長時間的遮蔽中斷,有可能導致資料的丟失甚至系統的崩潰。

1.2 使用

local_irq_disable()
local_irq_enable()
兩者配合只能禁止和使能本 CPU 內的中斷,這就是說,在解決 SMP 多 CPU 引起的競態的情況是無能為力的,在這種情況下適宜與自旋鎖聯合使用。
local_irq_save(flag)
local_irq_restore(flag)
禁止中斷,儲存目前的 CPU 的中斷位資訊
local_bh_disable()
locla_bh_enable()
只是禁止中斷的底半部

2. 原子操作:

原子操作指的是在執行過程中不會被別的程式碼路徑所中斷的操作。 或者 “可被中斷的一個或一系列操作” 原子操作分為兩類:整型原子操作、位原子操作

2.1 整型原子操作:

設定原子變數的值
void atomic_set(atomic_t *v, int i); /* 設定原子變數的值為 i */
atomic_t v = ATOMIC_INIT(0); /* 定義原子變數 v ,初始化為0 */
獲取原子變數的值
atomic_read(atomic_t *v); /* 返回原子變數的值 */
加/減
void atomic_add(int i, atomic_t *v); /* 原子變數增加i */
void atomic_sub(int i, atomic_t *v); /* 原子變數減少i */
自增/自減
void atomic_inc(atomic_t *v); /* 自加1 */
void atomic_dec(atomic_t *v); /* 自減1 */
操作並測試
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
對原子變數執行自增、自減和減操作後(無加)測試其是否為 0, 0,true !0,false 操作並返回
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
執行完操作後,返回新的值

2.2 位原子操作:

置位: -- 置1
void set_bit(nr, void *addr);
功能:
將 addr 地址的第 nr 位置為 1
清零
void clear_bit(nr, void *addr);
位翻轉
void change_bit(nr, void *addr);
測試
test_bit(nr, void *addr);
功能:
返回 addr 位的第 nr 位
測試並操作
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
功能:
相當於先執行了test_bit(nr, void *addr)後再執行 xxx_bit(nr, void *addr)

3. 原子操作的例項:

實現 裝置最多隻能被一個程序開啟:

關鍵的函式:

atomic_t v = ATOMIC_INIT(1);

static int hello_open (struct inode *inode, struct file *file)
{
    if (!atomic_dec_and_test(&v))
    {
        atomic_inc(&v);
        return -EBUSY;
    }
    printk (KERN_INFO "Hey! device opened\n");

    return 0;
}

static int hello_release (struct inode *inode, struct file *file)
{
    atomic_inc(&v); 
    printk (KERN_INFO "Hmmm! device closed\n");

    return 0;
}

函式說明:(hello_open函式)
①第一次開啟裝置驅動的時候 v = 1,v - 1 = 0 ,atomic_dec_and_test(&v) 結果是 true,不進迴圈,不返回EBUSY,執行下邊內容。 ②第二次開啟裝置驅動,v = 0, v - 1 = -1,atomic_dec_and_test(&v) 結果是 false,進迴圈,直接返回。