linux信號量機制

Linux 中的信號量(semphore)是一種資源鎖,如果有一個任務試圖獲得一個已經被佔用的信號量時,信號量會將其推到一個等待隊列中,這時處理器會重獲自由從而去執行其它代碼,當持有信號量的進程將信號量釋放後,處於等待隊列中的那個任務將會被喚醒,並將獲得該信號量。

  • 信號量分類
  • 信號量結構體定義
  • 信號量定義與初始化
  • 初始化一個計值信號量
  • 獲取一個信號量的函數
  • down_interruptible
  • 喚醒函數up

信號量分類
信號量分爲互斥信號量,以及計數信號量,區別在於 count的值,若爲1,則爲互斥信號量,若大於1 則爲計數信號量。


信號量結構體

1
2
3
4
5
struct semaphore {
       spinlock_t              lock;    //防止其它進程同時訪問該信號量,並禁止中斷
       unsigned int            count; //計值信號量
       struct list_head        wait_list; //加入等待隊列的指針
};


信號量定義與初始化

1
2
3
4
5
6
7
8
9
#define __SEMAPHORE_INITIALIZER(name,n)                                \
{                                                                       \
       .lock           =__SPIN_LOCK_UNLOCKED((name).lock),           \
       .count          = n,                                            \
       .wait_list      =LIST_HEAD_INIT((name).wait_list),            \
}
  
#define DEFINE_SEMAPHORE(name)  \
        struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)\

初始化一個計值信號量

1
2
3
4
5
6
static inline void sema_init(structsemaphore *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);
}

獲取一個信號量

1
2
3
4
5
6
extern void down(structsemaphore *sem);   //內核說已經過時了
extern int __must_check down_interruptible(struct semaphore *sem); //acquirethe semaphore unless interrupted, If the sleep is interrupted by a signal, thisfunction will return -EINTR.
extern int __must_check down_killable(struct semaphore *sem); //acquirethe semaphore unless killed, If the sleep is interrupted by a fatal signal,this function will return -EINTR
extern int __must_check down_trylock(struct semaphore *sem); //try toacquire the semaphore, without waiting
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);//acquire the semaphore within a specified time, If the semaphore is notreleased within the specified number of jiffies, this function returns -ETIME.
extern void up(structsemaphore *sem);

down_interruptible代碼分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
 *down_interruptible - acquire the semaphore unless interrupted
 *@sem: the semaphore to be acquired
 *
 *Attempts to acquire the semaphore.  If nomore tasks are allowed to
 *acquire the semaphore, calling this function will put the task to sleep.
 * If the sleep is interrupted by a signal, thisfunction will return -EINTR.
 * Ifthe semaphore is successfully acquired, this function returns 0.
 */
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--;      //計數值減1
       else
                result = __down_interruptible(sem);  //在切換前會開中斷,
       spin_unlock_irqrestore(&sem->lock, flags); //切換後開中斷
  
       return result;
}
EXPORT_SYMBOL(down_interruptible);
  
static noinline int __sched __down_interruptible(struct semaphore *sem)
{
       return __down_common(sem, TASK_INTERRUPTIBLE,MAX_SCHEDULE_TIMEOUT);
}
TASK_INTERRUPTIBLE 可被信號中斷喚醒
#define MAX_SCHEDULE_TIMEOUT    LONG_MAX 類似於無限睡下去
/*
 *Because this function is inlined, the 'state' parameter will be
 *constant, and thus optimised away by the compiler.  Likewise the
 *'timeout' parameter for the cases without timeouts.
 */
static inline int __sched__down_common(struct semaphore *sem, long state, long timeout)
{
       struct task_struct *task = current;
       struct semaphore_waiter waiter;
  
       list_add_tail(&waiter.list, &sem->wait_list); //將該任務加入到信號量的等待隊列中去
       waiter.task = task;
       waiter.up = 0;
  
       for (;;) {
                if (signal_pending_state(state,task)) //如果該進程被信號中斷,則退出,task是在哪被設置了這個任號的呢?遺留問題
                        goto interrupted;
                if (timeout <= 0)  //定時到了
                       goto timed_out;
                __set_task_state(task, state);  //將task的TASK_RUNNING狀態變成TASK_INTERRUPTIBEU狀態,
                                   當該進程被切換出去時,如果沒有被置爲TASK_RUNNING狀態,調度器則不會在調度它了
               spin_unlock_irq(&sem->lock); //吻合前面的spin_lock_irqsave,開中斷
                timeout =schedule_timeout(timeout); //將進程切換出去
               spin_lock_irq(&sem->lock);//關中斷,吻合後面的spin_unlock_irqsave
                if (waiter.up)
                        return 0;
       }
  
 timed_out:
       list_del(&waiter.list);
       return -ETIME;
  
 interrupted:
       list_del(&waiter.list);
       return -EINTR;
}
struct semaphore_waiter {
       struct list_head list;
       struct task_struct *task; //等待信號量的任務
       int up;
};

timeout= schedule_timeout(timeout); //將進程切換出去
當定時timeout時間到過後,返回值timeout變爲0, 則__down_common返回-ETIME.
現在假設有4個進程A B C D, A已經獲得一個信號量global,而B C D正在按順序等待該信號量(如果確實是這樣,會不會有點傻


喚醒函數,釋放信號量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * up- release the semaphore
 *@sem: the semaphore to release
 *
 *Release the semaphore.  Unlike mutexes,up() may be called from any
 *context and even by tasks which have never called down().
 */
void up(struct semaphore *sem)
{
       unsigned long flags;
  
       spin_lock_irqsave(&sem->lock, flags);
       if (likely(list_empty(&sem->wait_list)))   //
                sem->count++; //如果隊列爲空的話,則將計數值加1
       else
                __up(sem);   //喚醒
       spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
  
static noinline void __sched __up(structsemaphore *sem)
{
       struct semaphore_waiter *waiter =list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
       list_del(&waiter->list); //根據隊列,先進先出的原則,第一個down的進程會被先喚醒,將該進程從信號量的等待隊列中刪除
       waiter->up = 1;   //與down裏的if(waiter.up)對應
       wake_up_process(waiter->task); //喚醒進程,將其加入到可運行的進程鏈表中進程state的狀態由TASK_WAKING逐漸變成TASK_RUNNING中去
}
/**
 *wake_up_process - Wake up a specific process
 *@p: The process to be woken up.
 *
 *Attempt to wake up the nominated process and move it to the set of runnable
 *processes.  Returns 1 if the process waswoken up, 0 if it was already
 *running.
 *
 * Itmay be assumed that this function implies a write memory barrier before
 *changing the task state if and only if any tasks are woken up.
 */
int wake_up_process(struct task_struct *p)
{
       return try_to_wake_up(p, TASK_ALL, 0);
}
EXPORT_SYMBOL(wake_up_process);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章