1. 程式人生 > >(筆記)Linux內核學習(七)之內核同步機制和實現方式

(筆記)Linux內核學習(七)之內核同步機制和實現方式

mem head ron int 可用 ati 自旋 占用 void

一 原子操作

指令以原子的方式執行——執行過程不被打斷。

1 原子整數操作

原子操作函數接收的操作數類型——atomic_t

技術分享圖片 技術分享圖片
//定義
atomic_t v;
//初始化 atomic_t u = ATOMIC_INIT(0); //操作 atomic_set(&v,4); // v = 4 atomic_add(2,&v); // v = v + 2 = 6 atomic_inc(&v); // v = v + 1 = 7
//實現原子操作函數實現 static inline void atomic_add(int i, atomic_t *v) { unsigned long tmp; int result; __asm__ __volatile__("@ atomic_add\n"     "1: ldrex %0, [%3]\n"     " add %0, %0, %4\n"     " strex %1, %0, [%3]\n"     " teq %1, #0\n"     " bne 1b"   : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)   : "r" (&v->counter), "Ir" (i)   : "cc"); }
技術分享圖片 技術分享圖片

2 原子位操作

技術分享圖片 技術分享圖片
//定義
unsigned long word = 0;

//操作
set_bit(0,&word);       //第0位被設置1
set_bit(0,&word);       //第1位被設置1
clear_bit(1,&word);   //第1位被清空0

//原子位操作函數實現
static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
       unsigned long flags;
       unsigned long mask = 1UL << (bit & 31);

       p += bit >> 5;
       raw_local_irq_save(flags);
       *p |= mask;
       raw_local_irq_restore(flags);
}
技術分享圖片 技術分享圖片

二 自旋鎖

  原子位和原子整數僅能對簡單的整形變量進行原子操作,對於復雜的數據復雜的操作並不適用。

需要更復雜的同步方法實現保護機制——鎖。

  自旋鎖:同一時刻只能被一個可執行線程持有,獲得自旋鎖時,如果已被別的線程持有則該線程進行循環等待鎖重新可用,

然後繼續向下執行。

過程如下:

    技術分享圖片

使用鎖得基本形式如下:

技術分享圖片 技術分享圖片
spinlock_t lock;
//獲取鎖
spin_lock(&lock);
//臨界區
……
 
//釋放鎖
spin_unlock(&lock);
技術分享圖片 技術分享圖片

使用自旋鎖防止死鎖:

自旋鎖不可遞歸,自旋處於等待中造成死鎖;

中斷處理程序中,獲取自旋鎖前要先禁止本地中斷,中斷會打斷正持有自旋鎖的任務,中斷處理程序有可能爭用已經被持有的自旋鎖,造成死鎖。

讀寫自旋鎖:將鎖的用途直接分為讀取和寫入。

三 信號量

  信號量:睡眠鎖。如果有一個任務試圖獲取信號量時,

信號量未被占用:該任務獲得成功信號量;

信號量已被占用:信號量將任任務推進等待隊列,讓其睡眠,處理器繼續工作;當信號量被釋放後,

        喚醒信號量隊列中的任務,並獲取該信號量。

可有讀寫信號量。

      技術分享圖片

聲明信號量:

技術分享圖片 技術分享圖片
信號量數據結構:
/* Please don‘t access any members of this structure directly */
struct semaphore {

       raw_spinlock_t           lock;

       unsigned int        count;

       struct list_head   wait_list;

};
技術分享圖片 技術分享圖片

靜態聲明信號量:

技術分享圖片 技術分享圖片
//聲明可用數量為1信號量
#define DEFINE_SEMAPHORE(name)        struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

//聲明可用數量為n的信號量
#define __SEMAPHORE_INITIALIZER(name, n)                      {                                                                     .lock              = __RAW_SPIN_LOCK_UNLOCKED((name).lock),      
       .count           = n,                                    
       .wait_list       = LIST_HEAD_INIT((name).wait_list),           }
技術分享圖片 技術分享圖片

動態聲明信號量:

技術分享圖片 技術分享圖片
static inline void sema_init(struct semaphore *sem, int val)
{
    static struct lock_class_key __key;
    *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
    lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);

}
技術分享圖片 技術分享圖片

使用信號量:

技術分享圖片 技術分享圖片
  //初始化定義信號量
    struct semaphore driver_lock;
    sema_init(&driver_lock, 1);

    //獲取信號量
    if (down_interruptible(&driver_lock))
           return -EINTR;
    //執行臨界區
    ……

    //釋放信號量
    up(&driver_lock);
技術分享圖片 技術分享圖片

自旋鎖與信號量對比:

需求     使用鎖

低開銷加鎖 : 優先使用自旋鎖

短期鎖定 : 優先使用自旋鎖

長期鎖定 : 優先使用信號量

中斷上下文加鎖 : 使用自旋鎖

持有鎖需要睡眠 : 使用信號量

四 完成變量

  完成變量:如果在內核中一個任務需要發出信號通知另一個任務發生了某個特定事件,

       使用完成變量去喚醒在等待的任務,使兩個任務得以同步。信號量的簡易方法。

數據結構:

struct completion {
       unsigned int done;
       wait_queue_head_t wait;
};

完成變量聲明:

技術分享圖片 技術分享圖片
動態:#define COMPLETION_INITIALIZER(work)                             { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }

靜態: static inline void init_completion(struct completion *x)
{
       x->done = 0;
       init_waitqueue_head(&x->wait);
}
技術分享圖片 技術分享圖片

完成變量使用:

//等待制定的完成變量
void __sched wait_for_completion(struct completion *x)


//發信號喚醒等待的任務
void complete(struct completion *x)   

還有實現同步機制諸如:禁止搶占,Seq鎖(讀寫共享數據),順序和屏障……

(筆記)Linux內核學習(七)之內核同步機制和實現方式