__wake_up唤醒等待队列中的进程
等待队列头
struct __wait_queue_head
{
spinlock_t lock; //自旋锁用于保护自身资源(task_list链表)不被多个进程同时访问
struct list_head task_list;//双向循环链表通过此字段连接成等待对列链表
};
typedef struct __wait_queue_head wait_queue_head_t;
等待队列项
struct __wait_queue
{
//唤醒进程的方式 其中0是非互斥,WQ_FLAG_EXCLUSIVE(0×01)是互斥
当一个等待队列入口有 WQ_FLAG_EXCLUSEVE 标志置位, 它被添加到等待队列的尾部. 没有这个标志的入口项, 相反, 添加 到开始
当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
//私有指针变量,使用过程中会用来存放task_struct结构体
void *private;
//func是一个函数指针,指向用于唤醒队列中线程的函数。虽然提供了默认的唤醒函数default_wake_function,但也允许 灵活的设置队列的唤醒函数
wait_queue_func_t func;
//链表,用于将等待队列头、等待队列连接起来
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
唤醒队列中处于特定状态的进程
void __wake_up(wait_queue_head_t *q,//等待队列头
unsigned int mode, //进程的状态 如TASK_NORMAL
int nr_exclusive, //唤醒互斥进程的个数
void *key //一般设置为NULL(传给唤醒回调函数的参数在default_wake_function并未使用)
)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);//自旋锁上锁
//下面函数中会调用唤醒的回调函数
__wake_up_common(q, mode, nr_exclusive, 0, key);
spin_unlock_irqrestore(&q->lock, flags);//自旋锁解锁
}
同步唤醒队列中处于特定状态的进程
void __wake_up_sync(wait_queue_head_t *q,
unsigned int mode,
int nr_exclusive)
{
__wake_up_sync_key(q, mode, nr_exclusive, NULL);
}
void __wake_up_sync_key(wait_queue_head_t *q,
unsigned int mode,
int nr_exclusive,
void *key)
{
unsigned long flags;
int wake_flags = WF_SYNC;
if (unlikely(!q))
return;
if (unlikely(!nr_exclusive))
wake_flags = 0;
spin_lock_irqsave(&q->lock, flags);
__wake_up_common(q, mode, nr_exclusive, wake_flags, key);
spin_unlock_irqrestore(&q->lock, flags);
}
static void __wake_up_common(wait_queue_head_t *q, //等待队列头
unsigned int mode, //进程的状态TASK_NORMAL
int nr_exclusive, //唤醒互斥进程的个数
int wake_flags, //同步唤醒0 或异步唤醒1
void *key //一般设置为NULL(传给唤醒回调函数的参数在default_wake_function并未使用)
)
{
wait_queue_t *curr, *next;
//遍历等待队列中的数据项wait_queue_t类型
list_for_each_entry_safe(curr, next, &q->task_list, task_list)
{
unsigned flags = curr->flags;
(1)对队列中的每一个元素,调用其回调函数func:default_wake_function
(2)如果回调函数返回非0值,那么看队列元素的flag值,
如果设置了WQ_FLAG_EXCLUSIVE(互斥)标志,那么停止遍历,不继续唤醒其它进程;否则继续唤醒下一个进程
(3)如果设置了互斥进程唤醒个数nr_exclusive,则要唤醒nr_exclusive个互斥进程,若为<=0 则唤醒所有的
if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
调用关系:
//例子 wakeup的使用