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); |