6.3.1 linux 自旋鎖進(jìn)階

自旋鎖常用api

1,為什么除了spin_lock() api還定義了spin_lock_irq()等。

因?yàn)椋簊pin_lock()可以防止線程調(diào)度,但不能防止硬件中斷的到來,以及隨后的中斷處理函數(shù)(hardirq)的執(zhí)行。
假設(shè)一個CPU上的線程T持有了一個spinlock,發(fā)生中斷后,該CPU轉(zhuǎn)而執(zhí)行對應(yīng)的hardirq。如果該hardirq也試圖去持有這個spinlock,那么將無法獲取成功,導(dǎo)致hardirq無法退出。在hardirq主動退出之前,線程T是無法繼續(xù)執(zhí)行以釋放spinlock的,最終將導(dǎo)致該CPU上的代碼不能繼續(xù)向前運(yùn)行,形成死鎖(dead lock),
因此如果spin_lock()鎖住得臨界區(qū)可能被中斷打斷,要使用spin_lock_irq()或者spin_lock_irqsave()
但是即便使用了spin_lock_irqsave()只能關(guān)閉本CPU中斷,如果其他CPU上發(fā)生了中斷,那么這些CPU上的hardirq,也有可能試圖去獲取一個被本地CPU上運(yùn)行的線程T占有的spinlock。
不過沒有關(guān)系,因?yàn)榇藭rhardirq和線程T運(yùn)行在不同的CPU上,等到線程T繼續(xù)運(yùn)行釋放了這個spinlock,hardirq就有機(jī)會獲取到,不至于造成死鎖。

2,單核cpu使用spin_lock()會出現(xiàn)死鎖嗎。

不會,因?yàn)閟pin_lock()關(guān)閉了搶占,只能按照順序執(zhí)行,只要不發(fā)生中斷是不會發(fā)生死鎖的,但是若臨界區(qū)可能發(fā)生中斷仍然可能死鎖。

3,自旋鎖是怎么搶占的。
4,自旋鎖與互斥鎖的區(qū)別

自旋鎖和互斥鎖用于處理內(nèi)核中并發(fā)訪問,它們有各自的使用對象?!?br> 互斥鎖保護(hù)進(jìn)程的關(guān)鍵資源,而自旋鎖保護(hù)IRQ處理程序的關(guān)鍵部分?!せコ怄i讓競爭者在獲得鎖之前睡眠,而自旋鎖在獲得鎖之前一直自旋循環(huán)(消耗CPU)。·
鑒于上一點(diǎn),自旋鎖不能長時間持有,因?yàn)榈却咴诘却℃i期間會浪費(fèi)CPU時間;而互斥鎖則可以長時間持有,只要保護(hù)資源需要,
因?yàn)楦偁幷弑环湃氲却?duì)列中進(jìn)入睡眠狀態(tài)。[插圖]當(dāng)處理自旋鎖時,
請牢記:只是持有自旋鎖線程搶占被禁止,而自旋鎖的等待者沒有禁止搶占。

自旋鎖內(nèi)核實(shí)現(xiàn)

自旋鎖結(jié)構(gòu)體定義
typedef struct spinlock {
    union {
        struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
        struct {
            u8 __padding[LOCK_PADSIZE];
            struct lockdep_map dep_map;
        };
#endif
    };
} spinlock_t;

typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
#ifdef CONFIG_DEBUG_SPINLOCK
    unsigned int magic, owner_cpu;
    void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

typedef struct {
    union {
        u32 slock;
        struct __raw_tickets {
#ifdef __ARMEB__
            u16 next;
            u16 owner;
#else
            u16 owner;
            u16 next;
#endif
        } tickets;
    };
} arch_spinlock_t;
spin_lock_irqsave實(shí)現(xiàn)
#define spin_lock_irqsave(lock, flags)              \
do {                                \
    raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while (0)
#define raw_spin_lock_irqsave(lock, flags)          \
    do {                        \
        typecheck(unsigned long, flags);    \
        flags = _raw_spin_lock_irqsave(lock);   \
    } while (0)
對于單核
/*
 * 定義在include/linux/spinlock_api_up.h中 
 * 對于單核自旋鎖的功能就被弱化為禁止搶占,
 * 最終實(shí)際只執(zhí)行的是preempt_disable()-關(guān)閉搶占
 */
#define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags)

#define __LOCK_IRQSAVE(lock, flags) \
  do { local_irq_save(flags); __LOCK(lock); } while (0)
/*
* 在arm體系結(jié)構(gòu)中定義如下
 * In the UP-nondebug case there's no real locking going on, so the
 * only thing we have to do is to keep the preempt counts and irq
 * flags straight, to suppress compiler warnings of unused lock
 * variables, and to add the proper checker annotations:
 */
#define ___LOCK(lock) \
  do { __acquire(lock); (void)(lock); } while (0)
  
#define __LOCK(lock) \
  do { preempt_disable(); ___LOCK(lock); } while (0)
對于多核
/*
 * 定義在include/linux/spinlock_api_smp.h中 
 * 多核比較復(fù)雜,
 */
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    return flags;
}
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
    __acquire(lock);
    arch_spin_lock(&lock->raw_lock);
    mmiowb_spin_lock();
}
/*
 * 在arm體系結(jié)構(gòu)中
 *ARMv6 ticket-based spin-locking.
 * A memory barrier is required after we get a lock, and before we
 * release it, because V6 CPUs are assumed to have weakly ordered
 * memory.
 */

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;
    u32 newval;
    arch_spinlock_t lockval;

    prefetchw(&lock->slock);
    __asm__ __volatile__(
"1: ldrex   %0, [%3]\n"
"   add %1, %0, %4\n"
"   strex   %2, %1, [%3]\n"
"   teq %2, #0\n"
"   bne 1b"
    : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
    : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
    : "cc");

    while (lockval.tickets.next != lockval.tickets.owner) {
        wfe();
        lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
    }

    smp_mb();
}

參考:
https://www.ixigua.com/6940602677994717708?id=6872657862988923392&logTag=79b91f342c90aac06619
https://www.cnblogs.com/sky-heaven/p/13602357.html
https://mp.weixin.qq.com/s/viUgMAnVgC_bHyVifkHqsQ
https://mp.weixin.qq.com/s/mosYi_W-Rp1-HgdtxUqSEg

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

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

  • Spinlock 是內(nèi)核中提供的一種比較常見的鎖機(jī)制,自旋鎖是“原地等待”的方式解決資源沖突的,即,一個線程獲取了...
    日積跬步閱讀 733評論 0 0
  • POSIX threads(簡稱Pthreads)是在多核平臺上進(jìn)行并行編程的一套常用的API。線程同步(Thre...
    北辰青閱讀 1,061評論 0 1
  • 自旋鎖(spin lock)與互斥量(mutex)的比較 自旋鎖是一種非阻塞鎖,也就是說,如果某線程需要獲取自旋鎖...
    lfp901020閱讀 5,638評論 0 4
  • 1. 什么是自旋鎖 自旋鎖與互斥鎖有點(diǎn)類似,但是自旋鎖不會引起調(diào)用者阻塞,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者...
    cpp加油站閱讀 6,222評論 0 6
  • 1 臨界區(qū) 1.1簡介 在早期計(jì)算機(jī)系統(tǒng)中,只有一個任務(wù)進(jìn)程在執(zhí)行,并不存在資源的共享與競爭。隨著技術(shù)和需求的飛速...
    Fly晴天里Fly閱讀 9,189評論 2 13

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