Linux內核中的互斥操作(1)——信號量

*看了一段時間Linux內核源代碼了,經常會在代碼中看到down()、up()、spin_lock()、spin_unlock()、read_lock()、write_lock()、read_unlock()、write_unlock()等函數。本篇就先來看down()、up()是幹什麼的。。。它們的底層都是如何實現的。。。→_→*

  1. down()(P操作)

    內核中通過信號量(semaphore)來實現進程間對共享資源的互斥訪問,提供了down()函數(P操作)和up()函數(V操作)

    • 內核中信號量的數據結構
    //linux-2.4.0\include\asm-i386\Semaphore.h
    struct semaphore {
        atomic_t count;//計數器,表示可用資源的數量
        int sleepers;//等待進程的數量(其實只代表有沒有進程等待)
        wait_queue_head_t wait;//進程的等待隊列
    
    #if WAITQUEUE_DEBUG
    
        long __magic;
    
    #endif
    
    };
    • 初始化信號量
    
    #if WAITQUEUE_DEBUG
    
    
    # define __SEM_DEBUG_INIT(name) \
    
            , (int)&(name).__magic
    
    #else
    
    
    # define __SEM_DEBUG_INIT(name)
    
    
    #endif
    
    
    //初始化count與等待隊列
    
    #define __SEMAPHORE_INITIALIZER(name,count) \
    
    { ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
        __SEM_DEBUG_INIT(name) }
    
    //初始化信號量
    
    #define __MUTEX_INITIALIZER(name) \
    
        __SEMAPHORE_INITIALIZER(name,1)
    
    
    #define __DECLARE_SEMAPHORE_GENERIC(name,count) \
    
        struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
    
    //聲明初始值爲1的信號量
    
    #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
    
    //聲明初始值爲0的信號量
    
    #define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
    
    • down()
    static inline void down(struct semaphore * sem)
    {
    
    #if WAITQUEUE_DEBUG
    
        CHECK_MAGIC(sem->__magic);
    
    #endif
    
    
        __asm__ __volatile__(
            "# atomic down operation\n\t"
            //鎖總線,對count減1
            LOCK "decl %0\n\t"     /* --sem->count */
            "js 2f\n"
            "1:\n"//此時count大於等於0,返回down(),進入臨界區
            ".section .text.lock,\"ax\"\n"
            "2:\tcall __down_failed\n\t"//此時count小於0,調用__down_failed
            "jmp 1b\n"
            ".previous"
            :"=m" (sem->count)
            :"c" (sem)
            :"memory");
    }
    • __down_failed()中調用了__down()
    void __down(struct semaphore * sem)
    {
        struct task_struct *tsk = current;
        DECLARE_WAITQUEUE(wait, tsk);
        tsk->state = TASK_UNINTERRUPTIBLE;
        //將當前進程的等待隊列元素wait,鏈入隊列頭sem->wait的等待隊列的尾部
        add_wait_queue_exclusive(&sem->wait, &wait);
    
        spin_lock_irq(&semaphore_lock);
        sem->sleepers++;//將等待進入臨界區的進程數加1
        for (;;) {
            int sleepers = sem->sleepers;
    
            /*
             * Add "everybody else" into it. They aren't
             * playing, because we own the spinlock.
             */
             //執行__down()函數的進程是因爲沒有進入臨界區,但此時可能有進程已經執行了up(),所以有必要再一次檢查count,避免無謂的等待進入睡眠而浪費資源
             //atomic_add_negative()函數中執行sleepers-1加sem->count
             //若結果爲負數,返回非零,表示進程需要繼續等待
             //若結果不爲負數,返回零,表示不需要等待,可以進入臨界區
            if (!atomic_add_negative(sleepers - 1, &sem->count)) {
                sem->sleepers = 0;//設置等待進程數爲0
                break;//跳出循環
            }
            sem->sleepers = 1;  /* us - see -1 above *///設置等待進程數爲1,它在這裏只表示有無進程需要等待,而不表示有多少進程需要等待
            spin_unlock_irq(&semaphore_lock);
    
            schedule();//準備將此進程調度爲深度睡眠,即不會因爲信號而喚醒
            tsk->state = TASK_UNINTERRUPTIBLE;
            spin_lock_irq(&semaphore_lock);
        }
        spin_unlock_irq(&semaphore_lock);
        remove_wait_queue(&sem->wait, &wait);//將此進程移出等待隊列
        tsk->state = TASK_RUNNING;//設置此進程爲運行狀態
        wake_up(&sem->wait);//返回之前喚醒等待隊列中的其他進程
    }
  2. up()(V操作)

    • up()
    
    static inline void up(struct semaphore * sem)
    {
    
    #if WAITQUEUE_DEBUG
    
        CHECK_MAGIC(sem->__magic);
    
    #endif
    
        __asm__ __volatile__(
            "# atomic up operation\n\t"
            //鎖總線,對count加1,這和前面的atomic_add_negative()函數的作用又對起來了
            LOCK "incl %0\n\t"     /* ++sem->count */
            "jle 2f\n"
            "1:\n"
            ".section .text.lock,\"ax\"\n"
            "2:\tcall __up_wakeup\n\t"//當count小於等於0時,調用__up_wakeup()
            "jmp 1b\n"
            ".previous"
            :"=m" (sem->count)
            :"c" (sem)
            :"memory");
    }
    • __up_wakeup()中調用了__up(),__up()中調用了wake_up()
    //wake_up()是宏函數,其中調用了__wake_up()函數
    
    #define wake_up(x)          __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,WQ_FLAG_EXCLUSIVE)
    
    • __wake_up()
    //其中調用了__wake_up_common(),注意最後一個參數傳的是0
    void __wake_up(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode)
    {
        __wake_up_common(q, mode, wq_mode, 0);
    }
    • __wake_up_common()
    static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode,
                         unsigned int wq_mode, const int sync)
    {
        struct list_head *tmp, *head;
        struct task_struct *p, *best_exclusive;
        unsigned long flags;
        int best_cpu, irq;
    
        if (!q)
            goto out;
    
        best_cpu = smp_processor_id();
        irq = in_interrupt();
        best_exclusive = NULL;
        wq_write_lock_irqsave(&q->lock, flags);
    
        head = &q->task_list;
        tmp = head->next;
        while (tmp != head) {
            unsigned int state;
                    wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
            tmp = tmp->next;
            p = curr->task;
            state = p->state;
            if (state & mode) {
                /*
                 * If waking up from an interrupt context then
                 * prefer processes which are affine to this
                 * CPU.
                 */
                 //此函數的作用就是遍歷等待隊列,依次喚醒符合條件的進程,如果喚醒的進程TASK_EXCLUSIVE爲1,就停止喚醒其餘進程,被喚醒的進程在__down()中繼續執行
                if (irq && (curr->flags & wq_mode & WQ_FLAG_EXCLUSIVE)) {
                    if (!best_exclusive)
                        best_exclusive = p;
                    if (p->processor == best_cpu) {
                        best_exclusive = p;
                        break;
                    }
                } else {
                    if (sync)
                        wake_up_process_synchronous(p);
                    else
                        wake_up_process(p);
                    if (curr->flags & wq_mode & WQ_FLAG_EXCLUSIVE)
                        break;
                }
            }
        }
        if (best_exclusive) {
            if (sync)
                wake_up_process_synchronous(best_exclusive);
            else
                wake_up_process(best_exclusive);
        }
        wq_write_unlock_irqrestore(&q->lock, flags);
    out:
        return;
    }

    *先去吃個飯。。一會來繼續寫spin_lock()、spin_unlock()。。→_→*

發佈了107 篇原創文章 · 獲贊 36 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章