Chinaunix首页 | 论坛 | 博客
  • 博客访问: 437444
  • 博文数量: 123
  • 博客积分: 2686
  • 博客等级: 少校
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-23 22:11
文章分类
文章存档

2012年(3)

2011年(10)

2010年(100)

2009年(10)

我的朋友

分类: LINUX

2010-06-26 20:32:45

坚持理解一点,写一点。好记性不如烂笔头。

1. 等待队列
数据结构:
wait_queue_head_t 等待队列头。[include/linux/wait.h]

struct __wait_queue_head {
    spinlock_t lock;//等待队列对互斥访问
    struct list_head task_list;//某个等待队列的等待进程链表的头
};
typedef struct __wait_queue_head wait_queue_head_t;


等待队列实现在事件上的条件等待:希望等待特定事件对进程把自己放进合适的队列,并放弃控制权。因此,等待队列表示一组睡眠的进程,当某一个条件为真时,由内核唤醒他们。

wait_queue_t  等待队列的链表中的元素。[include/linux/wait.h]

typedef struct __wait_queue wait_queue_t;
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key);

struct __wait_queue {
    unsigned int flags;
#define WQ_FLAG_EXCLUSIVE    0x01
    void *private;
    wait_queue_func_t func;
    struct list_head task_list;
};


flags==1 表示当前进程是互斥进程,即互斥地使用要等待的资源,由kernel选择性地唤醒;flags==0表示是非互斥进程,总是由kernel在事件发生时唤醒。
func字段用来表示等待队列中睡眠进程应该用什么方式唤醒。

相关操作API:

DECLARE_WAIT_QUEUE_HEAD(name),定义一个新的等待队列头,并初始化对应的成员变量。[include/linux/wait.h]

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                \
    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),        \
    .task_list    = { &(name).task_list, &(name).task_list } }

#define DECLARE_WAIT_QUEUE_HEAD(name) \
    wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)


init_waitqueue_head(),初始化动态分配的等待队列的头变量。[kernel/wait.c]

void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *key)
{
    spin_lock_init(&q->lock);
    lockdep_set_class(&q->lock, key);
    INIT_LIST_HEAD(&q->task_list);
}


init_waitqueue_entry(q,p),初始化wait_queue_t结构的变量q,p是指向一个进程的指针。

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{
    q->flags = 0;
    q->private = p;
    q->func = default_wake_function;//非互斥线程将由
defalut_wake_function唤醒         

}


add_wait_queue(), 把一个非互斥进程插入等待队列链表的第一个位置。

add_wait_queue_exclusive(), 把一个互斥进程插入等待队列链表的最后一个位置。

remove_wait_queue()函数从等待队列链表中删除一个进程。

waitqueue_active()函数检查一个给定的等待队列是否为空。

要等待特定条件的进程可以调用如下列表中的任何一个函数

sleep_on, 对当前进程进行操作。

static long __sched
sleep_on_common(wait_queue_head_t *q, int state, long timeout)
{
    unsigned long flags;
    wait_queue_t wait;

    init_waitqueue_entry(&wait, current);

    __set_current_state(state);

    spin_lock_irqsave(&q->lock, flags);
    __add_wait_queue(q, &wait);
    spin_unlock(&q->lock);
    timeout = schedule_timeout(timeout);
    spin_lock_irq(&q->lock);
    __remove_wait_queue(q, &wait);
    spin_unlock_irqrestore(&q->lock, flags);

    return timeout;
}


void __sched sleep_on(wait_queue_head_t *q)

{

sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);

}

该函数把当前进程的状态设置成TASK_UNINTERRUPTIBLE,并把它插入到特定的等待队列。然后,它调用调度程序,而调度程序重新开始另一个程序的执行。当睡眠进程被唤醒,调度程序重新开始执行sleep_on函数,具体执行里面的 __remove_wait_queue(q, &wait),把该进程从等待队列中删除。

interruptile_sleep_on,与sleep_on函数一样,不同的是前者把当前进程的状态设置为TASK_INTERRUPTIBLE。接受一个信号就可以唤醒当前进程。

sleep_on_timeout() 和interruptible_sleep_on_timeout()与前面函数类似,但是他们允许调用者定义一个时间间隔,过了这个间隔以后,进程将由内核唤醒。他们调用schedule_timeout()而不是schedule()函数。

wait_event和wait_event_interruptible宏使他们的调用进程在等待队列上睡眠,一直到修改了给定条件为止。

说明:sleep_on()函数在以下条件下不能使用,那就是必须测试条件并且当条件还没有得到验证时,又紧接这让进程去睡眠由于那些条件是众所周知的竞争条件产生的根源。此外,为了把互斥进程插入等待队列,内核必须使用prepare_to_wait_exclusive()函数。所有其他函数把进程当做非互斥进程来插入。

内核通过以下任何一个宏来唤醒等待队列中的进程,并把他们的状态设置为TASK_RUNNING;
wake_up, wake_up_nr, wake_up_all, wake_up_interruptible, wake_up_interruptible_nr, wake_up_interruptible_all, wake_up_interruptible_sync, wake_up_locked.

#define wake_up(x)            __wake_up(x, TASK_NORMAL, 1, NULL)


/**

 * __wake_up - wake up threads blocked on a waitqueue.

 * @q: the waitqueue

 * @mode: which threads

 * @nr_exclusive: how many wake-one or wake-many threads to wake up

 * @key: is directly passed to the wakeup function

 *

 * It may be assumed that this function implies a write memory barrier before

 * changing the task state if and only if any tasks are woken up.

 */

void __wake_up(wait_queue_head_t *q, unsigned int mode,

int nr_exclusive, void *key)

{

unsigned long flags;


spin_lock_irqsave(&q->lock, flags);

__wake_up_common(q, mode, nr_exclusive, 0, key);

spin_unlock_irqrestore(&q->lock, flags);

}

/*

 * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just

 * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve

 * number) then we wake all the non-exclusive tasks and one exclusive task.

 *

 * There are circumstances in which we can try to wake a task which has already

 * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns

 * zero in this (rare) case, and we handle it by continuing to scan the queue.

 */

---------------------------------------------------------------

static void __wake_up_common(wait_queue_head_t *q, 

                             unsigned int mode,

     int nr_exclusive, 

                             int wake_flags, void *key)

{

wait_queue_t *curr, *next;


list_for_each_entry_safe(curr, next, &q->task_list, task_list) 

        {

unsigned flags = curr->flags;


if (curr->func(curr, mode, wake_flags, key) &&

(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)

break;

}

}


curr -> func ,等待队列中的每个结构体wait_queue_t, 这个结构体中的func字段存放唤醒函数的地址,它试图唤醒curr -> task 这个进程。如果一个进程已经被有效的唤醒并且进程是互斥的,循环结束,因为所有的非互斥进程总是在双向链表的开始位置,而所有的互斥进程在双向链表的尾部。
阅读(1729) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~