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

 

一 原子操做數據結構

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

1 原子整數操做this

       原子操做函數接收的操做數類型——atomic_tatom

複製代碼
//定義
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 原子位操做spa

複製代碼
//定義
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);
}
複製代碼

 

 

二 自旋鎖線程

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

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

  自旋鎖:同一時刻只能被一個可執行線程持有,得到自旋鎖時,若是已被別的線程持有則該線程進行循環等待鎖從新可用,blog

而後繼續向下執行。遞歸

過程以下:

      

    

 

 

使用鎖得基本形式以下:

複製代碼
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鎖(讀寫共享數據),順序和屏障……

相關文章
相關標籤/搜索