信號量機制DOWN操作和UP操作的詳細說明



DOWN操作:linux內核。信號DOWN例如,下面的操作:

  • void down(struct semaphore *sem); //不間斷
  • int down_interruptible(struct semaphore *sem);//可中斷
  • int down_killable(struct semaphore *sem);//睡眠的進程能夠由於受到致命信號而被喚醒,中斷獲取信號量的操作。

  • int down_trylock(struct semaphore *sem);//試圖獲取信號量,若無法獲得則直接返回1而不睡眠。

    返回0則 表示獲取到了信號量

  • int down_timeout(struct semaphore *sem,long jiffies);//表示睡眠時間是有限制的。假設在jiffies指明的時間到期時仍然無法獲得信號量,則將返回錯誤碼。

在以上四種函數中,驅動程序使用的最頻繁的就是down_interruptible函數,下面將對該函數進行分析。

down_interruptible函數的定義例如以下:

int down_interruptible(struct semaphore *sem)
{
       unsigned long flags;
       int result = 0;
       spin_lock_irqsave(&sem->lock,flags);
       if (likely(sem->count> 0))
              sem->count--;
       else
              result =__down_interruptible(sem);
       spin_unlock_irqrestore(&sem->lock,flags);
       return result;
}

函數分析:函數首先通過spin_lock_irqsave的調用來保證對sem->count操作的原子性。假設count>0。表示當前進程能夠獲得信號量,將count的值減1然后退出。

假設count不大於0,表明當前進程無法獲取信號量,則調用__down_interruptible,后者會繼續調用__down_common。

__down_common 函數定義例如以下:

static inline int __sched __down_common(struct semaphore *sem, longstate,
                                                        longtimeout)
{
       struct task_struct *task= current;
       struct semaphore_waiterwaiter;
       list_add_tail(&waiter.list,&sem->wait_list);
       waiter.task = task;
       waiter.up = 0; 
       for (;;) {
              if(signal_pending_state(state, task))
                     gotointerrupted;
              if (timeout <=0)
                     gototimed_out;
              __set_task_state(task,state);
              spin_unlock_irq(&sem->lock);
              timeout =schedule_timeout(timeout);
              spin_lock_irq(&sem->lock);
              if (waiter.up)
                     return 0;
       } 
 timed_out:
       list_del(&waiter.list);
       return -ETIME;
 interrupted:
       list_del(&waiter.list);
       return -EINTR;
}

函數分析:在__down_common函數數運行了下面操作。

(1)將當前進程放到信號量成員變量wait_list所管理的隊列中。

(2)在一個for循環中把當前的進程狀態這是為TASK_INTERRUPTIBLE,在調用schedule_timeout使當前進程進入睡眠狀態。函數將停留在schedule_timeout調用上,知道再次被調度運行。

(3) 當該進程再一次被調度時,按原因運行對應的操作:假設waiter.up不為0說明進程被該信號量的up操作所喚醒,進程能夠獲得信號量。假設進程是由於被用戶空間的信號所中斷或超時信號所引起的喚醒。則返回對應的錯誤代碼。

 

UP操作:LINUX內核僅僅提供了一個up函數

up函數定義例如以下:

void up(struct semaphore *sem)
{
       unsigned long flags;
 
       spin_lock_irqsave(&sem->lock,flags);
       if(likely(list_empty(&sem->wait_list)))
              sem->count++;
       else
              __up(sem);
       spin_unlock_irqrestore(&sem->lock,flags);
}

函數分析:假設sem的wait_list隊列為空,則表明沒有其它進程正在等待該信號量,那么僅僅須要把sem的count加1就可以。假設wait_list隊列不為空,則說明有其它進程正睡眠在wait_list上等待該信號。此時調用__up(sem)來喚醒進程:

__up()函數定義例如以下:

static noinline void __sched __up(struct semaphore *sem)
{
       struct semaphore_waiter*waiter = list_first_entry(&sem->wait_list,
                                          structsemaphore_waiter, list);
       list_del(&waiter->list);
       waiter->up = 1;
       wake_up_process(waiter->task);
}

函數分析:在函數中,調用了wake_up_process來喚醒進程,這樣進程就從之前的__down_interruptible調用中的timeout=schedule_timeout(timeout)處醒來,wait-up=1, __down_interruptible返回0。進程獲得了信號量。

up()與down()函數之間的聯系:由上面對兩個函數的分析能夠知道,__down_common函數中timeout=schedule_timeout(timeout) 它具有非常重要的數據。

版權聲明:本文博主原創文章,博客,未經同意不得轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM