- 論壇徽章:
- 0
|
等待隊(duì)列適用于通知其他相關(guān)任務(wù)某個(gè)事件的發(fā)生。這里的分析實(shí)例是等待物理頁面解鎖的函數(shù)
wait_on_page(),該函數(shù)判斷指定的物理頁面是否已經(jīng)加鎖。若已經(jīng)加鎖,則任務(wù)進(jìn)入等待狀態(tài),直到物理頁面被解鎖后才返回。
等待隊(duì)列相關(guān)的數(shù)據(jù)結(jié)構(gòu)。
等待隊(duì)列頭:
struct __wait_queue_head {
wa_lock_t lock; // wa_lock_t實(shí)際上是一個(gè)“讀寫鎖”或“旋轉(zhuǎn)鎖”
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
等待隊(duì)列節(jié)點(diǎn):
struct __wait_queue {
unsigned int flags;
struct task_list * task;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
1. 等待解鎖:
wait_on_page()實(shí)際上只是檢查頁面是否已經(jīng)被鎖。假如沒有加鎖,立即返回;否則,調(diào)用__wait_on_page()函數(shù)將當(dāng)前任務(wù)加入等待隊(duì)列中:
extern inline void wait_on_page(struct page * page)
{
if (PageLocked(page))
__wait_on_page(page);
}
__wait_on_page()函數(shù):
void ___wait_on_page(struct page *page)
{
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
add_wait_queue(&page->wait, &wait);
do {
sync_page(page);
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (!PageLocked(page))
break;
run_task_queue(&tq_disk);
schedule();
} while (PageLocked(page));
tsk->state = TASK_RUNNING;
remove_wait_queue(&page->wait, &wait);
}
宏DECLARE_WAITQUEUE()的定義如下:
#define __WAITQUEUE_INITIALIZER(name,task) \
{ 0x0, task, { NULL, NULL } __WAITQUEUE_DEBUG_INIT(name)}
#define DECLARE_WAITQUEUE(name,task) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name,task)
DECLARE_WAITQUEUE(name, task)宏定義了一個(gè)等待隊(duì)列節(jié)點(diǎn),并初始化wait_queue_t::task指向當(dāng)前任務(wù)的task結(jié)構(gòu)。以便在事件發(fā)生的時(shí)候能夠喚醒該task對應(yīng)的任務(wù)。
接下來便是將等待隊(duì)列節(jié)點(diǎn)wait加入page::wait等待隊(duì)列中:
void add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)
{
unsigned long flags;
wq_write_lock_irqsave(&q->lock, flags);
wait->flags = 0;
__add_wait_queue(q, wait);
wq_write_unlock_irqrestore(&q->lock, flags);
}
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
add_wait_queue
()函數(shù)返回后,wait已經(jīng)加入到page::wait等待隊(duì)列中。接著調(diào)用了sync_page(),其功用在此不做討論。
再往下執(zhí)行,函數(shù)對任務(wù)標(biāo)
志做了設(shè)置:set_task_state(tsk,
TASK_UNINTERRUPTIBLE);意為通知調(diào)度器將任務(wù)轉(zhuǎn)入睡眠狀態(tài)。由于在以上的操作中,頁面可能已經(jīng)解鎖了,所以
__wait_on_page()函數(shù)在調(diào)用schedule()重新調(diào)度任務(wù)之前再做一次頁面加鎖檢查,以避免任務(wù)等待一個(gè)不加鎖的頁面而導(dǎo)致死鎖。本
次檢查中若頁面仍然處于加鎖狀態(tài),則調(diào)用schedule()將自己轉(zhuǎn)入睡眠,直到任務(wù)再次被喚醒。
2. 解鎖通知:
任務(wù)通過調(diào)用UnlockPage對指定的頁面解鎖。UnlockPage實(shí)際上是一個(gè)宏定義:
#define UnlockPage(page) do { \
smp_mb__before_clear_bit(); \
if (!test_and_clear_bit(PG_locked, &(page)->flags)) BUG(); \
smp_mb__after_clear_bit(); \
if (waitqueue_active(&page->wait)) \
wake_up(&page->wait); \
} while (0)
首先,調(diào)用test_and_clear_bit()清除加鎖標(biāo)志位。然后檢查是否有任務(wù)在等待頁面。若有,調(diào)用wake_up()喚醒等待任務(wù)。
static inline int waitqueue_active(wait_queue_head_t *q)
{
return !list_empty(&q->task_list);
}
#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,WQ_FLAG_EXCLUSIVE)
void __wake_up(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode)
{
__wake_up_common(q, mode, wq_mode, 0);
}
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.
*/
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;
}
__wake_up_common
()函數(shù)里面有個(gè)while循環(huán),該循環(huán)遍歷等待隊(duì)列里面的所有節(jié)點(diǎn)(具體情況仍然由任務(wù)的屬性、是否處于中斷處理等環(huán)境有關(guān))。由于__wake_up()調(diào)用__wake_up_common()函數(shù)的時(shí)候,
傳遞進(jìn)來的mode是(TASK_UNINTERRUPTIBLE|TASK_INTERRUPTIBLE),
wq_mode是WQ_FLAG_EXCLUSIVE,
sync是0,所以最終每個(gè)隊(duì)列節(jié)點(diǎn)的任務(wù)結(jié)構(gòu)指針都會被作為參數(shù)傳遞給wake_up_process(),顧名思義,這個(gè)函數(shù)的作用就是喚醒任務(wù)了。而此時(shí),調(diào)用schedule()函數(shù)的任務(wù)就可以從睡眠狀態(tài)轉(zhuǎn)到運(yùn)行狀態(tài),schedule()函數(shù)返回。
關(guān)于wake_up_process()函數(shù)所進(jìn)行的操所,主要就是將任務(wù)結(jié)構(gòu)放入運(yùn)行隊(duì)列中,讓調(diào)度器運(yùn)行該任務(wù)。
wake_up_process()函數(shù):
void wake_up_process(struct task_struct * p)
{
unsigned long flags;
/*
* We want the common case fall through straight, thus the goto.
*/
spin_lock_irqsave(&runqueue_lock, flags);
p->state = TASK_RUNNING;
if (task_on_runqueue(p))
goto out;
add_to_runqueue(p);
reschedule_idle(p);
out:
spin_unlock_irqrestore(&runqueue_lock, flags);
}
本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u1/35101/showart_485204.html |
|