一. linux kernel阻塞等待事件方法:
把线程置为可中断或者不可中断状态
然后调用进程调度函数执行其他代码
该进程代码再次执行的情况:
1) 调度超时
2) 进程被等待事件唤醒
3) 进程设置可中断状态,被某个信号中断了
4) CPU空闲调度
此时需要判断进程是否被等待事件唤醒, 是的话则返回. 否则重复上述步骤.
二. linux kernel等待事件唤醒
调用 try_to_wake_up(task, TASK_NORMAL, 0) 函数即可唤醒进程.
三. 为了能够实现多个进程等待一个资源, 就需要一个等待队列以及一个计数器.
计数器用来指示要唤醒多少个等待资源的进程.
每个等待资源的进程都会添加到等待队列中, 包含标志,回调函数以及私有数据. 一般来说, 私有数据是进程的结构体, 回调函数是default_wake_function(用来唤醒进程).
struct completion {
unsigned int done; // 唤醒几个进程
wait_queue_head_t wait; // 等待队列头
};
struct __wait_queue_head {
spinlock_t lock; // 用来保护链表
struct list_head task_list; // 链表头
};
typedef struct __wait_queue_head wait_queue_head_t;
// 实际等待队列的结构体, 标志, 回调, 私有数据, 链表
struct __wait_queue {
unsigned int flags;
void *private; // task_struct
wait_queue_func_t func; // default_wake_function 唤醒进程
struct list_head task_list; // 链表
};
complete 唤醒函数的实现:
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++; // 唤醒单个进程
__wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
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;
}
}
========================
wait_for_completion_timeout 等待资源函数实现:
unsigned long
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
// 设置进程为不可中断状态
return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
}
static long wait_for_common(struct completion *x, long timeout, int state)
{
spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, timeout, state);
spin_unlock_irq(&x->wait.lock);
return timeout;
}
static inline long
do_wait_for_common(struct completion *x, long timeout, int state)
{
if (!x->done) {
// 申明wait_queue_t结构体宏, 设置私有结构体为本进程task, 回调为唤醒函数
DECLARE_WAITQUEUE(wait, current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
// 添加到等待队列链表
__add_wait_queue_tail(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = schedule_timeout(timeout);
spin_lock_irq(&x->wait.lock);
// 没唤醒, 也没超时则循环等待
} while (!x->done && timeout);
// 退出等待队列
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
// 消耗一个资源
x->done--;
return timeout ?: 1;
}
================================ 简单例子 ========================================
// 申明completion结构体, 并初始化锁等资源
DECLARE_COMPLETION(work);
进程上下文, 等待资源
static void wait_resource()
{
// 由于这个函数使用局部变量的等待队列, 所以它是可重入的.
wait_for_completion_timeout(&work, msecs_to_jiffies(1000));
}
// 中断函数, 唤醒等待的进程
static irqreturn_t irq_handler(int irq, void *cookie)
{
complete(work);
}
把线程置为可中断或者不可中断状态
然后调用进程调度函数执行其他代码
该进程代码再次执行的情况:
1) 调度超时
2) 进程被等待事件唤醒
3) 进程设置可中断状态,被某个信号中断了
4) CPU空闲调度
此时需要判断进程是否被等待事件唤醒, 是的话则返回. 否则重复上述步骤.
二. linux kernel等待事件唤醒
调用 try_to_wake_up(task, TASK_NORMAL, 0) 函数即可唤醒进程.
三. 为了能够实现多个进程等待一个资源, 就需要一个等待队列以及一个计数器.
计数器用来指示要唤醒多少个等待资源的进程.
每个等待资源的进程都会添加到等待队列中, 包含标志,回调函数以及私有数据. 一般来说, 私有数据是进程的结构体, 回调函数是default_wake_function(用来唤醒进程).
struct completion {
unsigned int done; // 唤醒几个进程
wait_queue_head_t wait; // 等待队列头
};
struct __wait_queue_head {
spinlock_t lock; // 用来保护链表
struct list_head task_list; // 链表头
};
typedef struct __wait_queue_head wait_queue_head_t;
// 实际等待队列的结构体, 标志, 回调, 私有数据, 链表
struct __wait_queue {
unsigned int flags;
void *private; // task_struct
wait_queue_func_t func; // default_wake_function 唤醒进程
struct list_head task_list; // 链表
};
complete 唤醒函数的实现:
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++; // 唤醒单个进程
__wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
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;
}
}
========================
wait_for_completion_timeout 等待资源函数实现:
unsigned long
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
// 设置进程为不可中断状态
return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
}
static long wait_for_common(struct completion *x, long timeout, int state)
{
spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, timeout, state);
spin_unlock_irq(&x->wait.lock);
return timeout;
}
static inline long
do_wait_for_common(struct completion *x, long timeout, int state)
{
if (!x->done) {
// 申明wait_queue_t结构体宏, 设置私有结构体为本进程task, 回调为唤醒函数
DECLARE_WAITQUEUE(wait, current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
// 添加到等待队列链表
__add_wait_queue_tail(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = schedule_timeout(timeout);
spin_lock_irq(&x->wait.lock);
// 没唤醒, 也没超时则循环等待
} while (!x->done && timeout);
// 退出等待队列
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
// 消耗一个资源
x->done--;
return timeout ?: 1;
}
================================ 简单例子 ========================================
// 申明completion结构体, 并初始化锁等资源
DECLARE_COMPLETION(work);
进程上下文, 等待资源
static void wait_resource()
{
// 由于这个函数使用局部变量的等待队列, 所以它是可重入的.
wait_for_completion_timeout(&work, msecs_to_jiffies(1000));
}
// 中断函数, 唤醒等待的进程
static irqreturn_t irq_handler(int irq, void *cookie)
{
complete(work);
}