6.3 Linux內(nèi)核自旋鎖

  • Spinlock 是內(nèi)核中提供的一種比較常見的鎖機(jī)制,自旋鎖是“原地等待”的方式解決資源沖突的,即,一個(gè)線程獲取了一個(gè)自旋鎖后,另外一個(gè)線程期望獲取該自旋鎖,獲取不到,只能夠原地“打轉(zhuǎn)”(忙等待)。由于自旋鎖的這個(gè)忙等待的特性,注定了它使用場(chǎng)景上的限制 —— 自旋鎖不應(yīng)該被長(zhǎng)時(shí)間的持有(消耗 CPU 資源)。
  • 為了避免因?yàn)檎{(diào)度可能自旋的線程而浪費(fèi)CPU,可以嘗試獲取從運(yùn)行隊(duì)列移出的其他線程持有的鎖,只要持有自旋鎖的代碼正在運(yùn)行,內(nèi)核就會(huì)禁止搶占。禁止搶占可以防止自旋鎖持有者被移出運(yùn)行隊(duì)列,這會(huì)導(dǎo)致等待進(jìn)程長(zhǎng)時(shí)間自旋而消耗CPU。
  • 只要有一個(gè)任務(wù)持有自旋鎖,其他任務(wù)就可能在等待的時(shí)候自旋。用自旋鎖時(shí),必須確保不會(huì)長(zhǎng)時(shí)間持有它??赡苡腥藭?huì)說在循環(huán)中自旋所浪費(fèi)CPU時(shí)間,比線程進(jìn)入睡眠,上下文切換到其他線程或進(jìn)程,然后再喚醒所浪費(fèi)的CPU更好一些。在一個(gè)處理器上自旋意味著在該處理器上不能再運(yùn)行其他任何任務(wù);在單核機(jī)器上使用自旋鎖是沒有任何意義的。最佳情況下,系統(tǒng)可能會(huì)變慢,最糟情況下,和互斥鎖一樣會(huì)造成死鎖。正是因?yàn)檫@個(gè)原因,內(nèi)核在處理單個(gè)處理器上的spin_lock(spinlock_t *lock)調(diào)用時(shí)將禁止搶占。在單個(gè)處理器(核)系統(tǒng)上,應(yīng)該使用spin_lock_irqsave()和spin_unlock_ irqrestore(),它們分別禁用處理器上中斷,防止中斷并發(fā)。
  • 由于事先并不知道所寫驅(qū)動(dòng)程序運(yùn)行在什么系統(tǒng)上,因此建議使用spin_lock_irqsave (spinlock_t *lock, unsigned long flags)獲取自旋鎖,該函數(shù)會(huì)在獲取自旋鎖之前,禁止當(dāng)前處理器(調(diào)用該函數(shù)的處理器)上中斷。spin_lock_irqsave在內(nèi)部調(diào)用local_irq_save (flags)和preempt_disable(),前者是一個(gè)依賴于體系結(jié)構(gòu)的函數(shù),用于保存IRQ狀態(tài),后者禁止在相關(guān)CPU上發(fā)生搶占。然后應(yīng)該用spin_unlock_irqrestore()釋放鎖,它執(zhí)行的操作與我們前面列舉的相反。

自旋鎖的特點(diǎn)

1.當(dāng)發(fā)生訪問資源沖突的時(shí)候,可以有兩個(gè)選擇:一個(gè)是死等,一個(gè)是掛起當(dāng)前進(jìn)程,調(diào)度其他進(jìn)程執(zhí)行。spin lock是一種死等的機(jī)制.
2.只允許一個(gè)thread進(jìn)入。semaphore可以允許多個(gè)thread進(jìn)入,spin lock不行,一次只能有一個(gè)thread獲取鎖并進(jìn)入臨界區(qū),其他的thread都是在門口不斷的嘗試。
3.臨界區(qū)執(zhí)行時(shí)間要短,臨界區(qū)執(zhí)行時(shí)間過長(zhǎng),等待該自旋鎖空等的時(shí)間越長(zhǎng),浪費(fèi)CPU資源。
4.可以在中斷上下文執(zhí)行。由于不睡眠,因此spin lock可以在中斷上下文中適用。

自旋鎖的定義

struct liblockdep_pthread_mutex {
    pthread_mutex_t mutex;
    struct lock_class_key key;
    struct lockdep_map dep_map;
};

typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t;
#define pthread_mutex_t         liblockdep_pthread_mutex_t
#define spinlock_t      pthread_mutex_t

static __always_inline void spin_lock(spinlock_t *lock)
{
    raw_spin_lock(&lock->rlock);
}

spin_lock的調(diào)用關(guān)系
spin_lock
|
+ -----> raw_spin_lock
|
+------> _raw_spin_lock
|
+--------> __raw_spin_lock

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

spin_lock_irq的調(diào)用關(guān)系
spin_lock_irq
|
+ -----> raw_spin_lock_irq
|
+------> _raw_spin_lock_irq
|
+--------> __raw_spin_lock_irq

static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
    local_irq_disable();
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

spin_trylock最終調(diào)用的是__raw_spin_trylock,成功返回1,獲取失敗返回0

static inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
    preempt_disable();
    if (do_raw_spin_trylock(lock)) {
        spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
        return 1;
    }
    preempt_enable();
    return 0;
}

自旋鎖的使用

1 定義自旋鎖變量 spinlock_t testlock;

2 初始化鎖 spin_lock_init(&testlock);

以上兩步也可以通過DEFINE_SPINLOCK(testlock)來實(shí)現(xiàn)

3 獲取鎖 spin_lock/spin_lock_irq/spin_lock_irqsave

4 釋放鎖 spin_unlock/spin_unlock_irq/spin_unlock_restore

實(shí)例

drivers/input/keyboard/gpio_keys.c

struct gpio_button_data {
        ......
    spinlock_t lock;
        ......
};

spin_lock_init(&bdata->lock);

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
    struct gpio_button_data *bdata = dev_id;
    struct input_dev *input = bdata->input;
    unsigned long flags;

    BUG_ON(irq != bdata->irq);

    spin_lock_irqsave(&bdata->lock, flags);

    if (!bdata->key_pressed) {
        if (bdata->button->wakeup)
            pm_wakeup_event(bdata->input->dev.parent, 0);

        input_event(input, EV_KEY, *bdata->code, 1);
        input_sync(input);

        if (!bdata->release_delay) {
            input_event(input, EV_KEY, *bdata->code, 0);
            input_sync(input);
            goto out;
        }

        bdata->key_pressed = true;
    }

    if (bdata->release_delay)
        hrtimer_start(&bdata->release_timer,
                  ms_to_ktime(bdata->release_delay),
                  HRTIMER_MODE_REL_HARD);
out:
    spin_unlock_irqrestore(&bdata->lock, flags);
    return IRQ_HANDLED;
}

drivers/cpuidle/cpuidle-ux500.c

void nv50_crc_handle_vblank(struct nv50_head *head)
{
        ......
    /*
     * We don't lose events if we aren't able to report CRCs until the
     * next vblank, so only report CRCs if the locks we need aren't
     * contended to prevent missing an actual vblank event
     */
    if (!spin_trylock(&crc->lock))
        return;

    if (!crc->src)
        goto out;

  out:
    spin_unlock(&crc->lock);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容