免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1401 | 回复: 0
打印 上一主题 下一主题

linux内核学习笔记-等待队列 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-02-27 21:48 |只看该作者 |倒序浏览

                等待队列适用于通知其他相关任务某个事件的发生。这里的分析实例是等待物理页面解锁的函数
wait_on_page(),该函数判断指定的物理页面是否已经加锁。若已经加锁,则任务进入等待状态,直到物理页面被解锁后才返回。
等待队列相关的数据结构。
等待队列头:
struct __wait_queue_head {
    wa_lock_t lock;                           // wa_lock_t实际上是一个“读写锁”或“旋转锁”
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
等待队列节点:
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()实际上只是检查页面是否已经被锁。假如没有加锁,立即返回;否则,调用__wait_on_page()函数将当前任务加入等待队列中:
extern inline void wait_on_page(struct page * page)
{
    if (PageLocked(page))
        __wait_on_page(page);
}
__wait_on_page()函数:
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)宏定义了一个等待队列节点,并初始化wait_queue_t::task指向当前任务的task结构。以便在事件发生的时候能够唤醒该task对应的任务。
接下来便是将等待队列节点wait加入page::wait等待队列中:
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
()函数返回后,wait已经加入到page::wait等待队列中。接着调用了sync_page(),其功用在此不做讨论。
再往下执行,函数对任务标
志做了设置:set_task_state(tsk,
TASK_UNINTERRUPTIBLE);意为通知调度器将任务转入睡眠状态。由于在以上的操作中,页面可能已经解锁了,所以
__wait_on_page()函数在调用schedule()重新调度任务之前再做一次页面加锁检查,以避免任务等待一个不加锁的页面而导致死锁。本
次检查中若页面仍然处于加锁状态,则调用schedule()将自己转入睡眠,直到任务再次被唤醒。
2. 解锁通知:
任务通过调用UnlockPage对指定的页面解锁。UnlockPage实际上是一个宏定义:
#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)
首先,调用test_and_clear_bit()清除加锁标志位。然后检查是否有任务在等待页面。若有,调用wake_up()唤醒等待任务。
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
()函数里面有个while循环,该循环遍历等待队列里面的所有节点(具体情况仍然由任务的属性、是否处于中断处理等环境有关)。由于__wake_up()调用__wake_up_common()函数的时候,
传递进来的mode是(TASK_UNINTERRUPTIBLE|TASK_INTERRUPTIBLE),
wq_mode是WQ_FLAG_EXCLUSIVE,
sync是0,所以最终每个队列节点的任务结构指针都会被作为参数传递给wake_up_process(),顾名思义,这个函数的作用就是唤醒任务了。而此时,调用schedule()函数的任务就可以从睡眠状态转到运行状态,schedule()函数返回。
关于wake_up_process()函数所进行的操所,主要就是将任务结构放入运行队列中,让调度器运行该任务。
wake_up_process()函数:
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博客,如果查看原文请点:http://blog.chinaunix.net/u1/35101/showart_485204.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP