linux基础之completion
来源:互联网 发布:eclipse端口号 编辑:程序博客网 时间:2024/06/02 02:02
completion也是一种同步机制,与信号量类似。
信号量可以用于实现同步,但往往可能会出现一些不好的结果。例如:当进程A分配了一个临时信号量变量,把它初始化为关闭的MUTEX,并把其地址传递给进程B,然后在A之上调用down(),进程A打算一旦被唤醒就撤销给信号量。随后,运行在不同CPU上的进程B在同一个信号量上调用up()。然而,up()和down()的目前实现还允许这两个函数在同一个信号量上并发。因此,进程A可以被唤醒并撤销临时信号量,而进程B还在运行up()函数。结果p()可能试图访问一个不存在的数据结构。这样就会出现错误。为了防止发生这种错误就专门设计了completion机制专门用于同步。
关于completion 比信号量完善。专业解释如下
http://bbs.chinaunix.net/thread-4156398-1-1.html
http://lkml.iu.edu//hypermail/linux/kernel/0107.3/0674.html,google上应该还有其它的邮件。
那时的semaphore是没有锁保护的。到少我手上2.6.24的源码里就没有。
现在semaphore已经没有问题了。甚至有人还提议用semaphore从新实现completion:
http://lwn.net/Articles/277621/
从git来看,最终社区并没有接纳。
现在高版本的semaphore 已经不存在问题了。只是从概念层面,completion 和semaphore 应该被用来不同的
场合,completion 是用来等待一个条件成力,而semaphore应该是等待一个资源。条件是没有数量的,而资源
是有数量的。从实现来说,completion更轻(我没有验正过)
请在http://www.makelinux.net/ldd3/chp-5-sect-4里搜lightweight。
自己理解不是太好,认为释放似乎不是问题,主要是功能有些差别。关于释放踩空问题,自己理解如下:因为completion机制是定义一个队列头,然后每个进程往这个队列头里添加队列项,所以即使线程A将队列项地址传递给线程B后又销毁了这个地址时,线程B调用complete也不会踩到空地址。不会发生crash
定义
static struct completion marlin_ack = {0};
初始化
init_completion (&marlin_ack);
wait_for_completion等待在completion上;如果加了interruptible,就表示线程等待可被外部发来的信号打断;如果加了killable,就表示线程只可被kill信号打断;如果加了timeout,表示等待超出一定时间会自动结束等待,timeout的单位是系统所用的时间片jiffies(多为1ms)。try_wait_for_completion则是非阻塞地获取completion;completion_done检查是否有线程阻塞在completion上;
ret = wait_for_completion_timeout( &marlin_ack, msecs_to_jiffies(100) );
complete唤醒阻塞在completion上的首个线程;complete_all唤醒阻塞在completion上的所有线程。
complete(&marlin_ack);
看源码就知道了:
/kernel/include/linux/completion.h
/**
* init_completion - Initialize a dynamically allocated completion
* @x: completion structure that is to be initialized
*
* This inline function will initialize a dynamically created completion
* structure.
*/
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
./kernel/kernel/wait.c
void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
{
spin_lock_init(&q->lock);
lockdep_set_class_and_name(&q->lock, key, name);
INIT_LIST_HEAD(&q->task_list);
}
kernel/kernel/sched/core.c
/**
* wait_for_completion: - waits for completion of a task
* @x: holds the state of this particular completion
*
* This waits to be signaled for completion of a specific task. It is NOT
* interruptible and there is no timeout.
*
* See also similar routines (i.e. wait_for_completion_timeout()) with timeout
* and interrupt capability. Also see complete().
*/
void __sched wait_for_completion(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion);
static long __sched
wait_for_common(struct completion *x, long timeout, int state)
{
return __wait_for_common(x, schedule_timeout, timeout, state);
}
static inline long __sched
__wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
might_sleep();
spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, action, timeout, state);
spin_unlock_irq(&x->wait.lock);
return timeout;
}
static inline long __sched
do_wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);
__add_wait_queue_tail_exclusive(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = action(timeout); // 这里action 函数即是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;
}
/**
* complete: - signals a single thread waiting on this completion
* @x: holds the state of this particular completion
*
* This will wake up a single thread waiting on this completion. Threads will be
* awakened in the same order in which they were queued.
*
* See also complete_all(), wait_for_completion() and related routines.
*
* 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 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);
}
/*
* 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) &&//这个func是 DECLARE_WAITQUEUE时,默认的default_wake_function
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
- linux基础之completion
- Linux内核同步机制之completion
- Linux内核同步机制之completion
- Linux内核同步机制之completion
- Linux内核同步机制之completion
- Linux内核同步机制之completion
- Linux内核同步机制之completion
- linux completion
- Linux Completion
- Linux 的 completion机制
- Linux中的completion
- linux completion接口
- Linux中的completion
- linux kernel completion
- linux completion接口
- Linux completion理解
- linux bash-completion
- Linux 的 completion机制
- jQuery ajax - serialize() 方法
- leetcode 397. Integer Replacement
- 关于double转string出现科学计数法的问题
- 多进程与多线程的区别与选择(转强力推荐)
- 手把手教你使用Git
- linux基础之completion
- react native 自定义基础组件
- 滑动 CollectionView 图片混乱问题
- GOF23的一些总结(十九)
- CTF解题笔记(1)
- Sphinx 的介绍和原理探索
- 移动互联培训总结
- GO语言学习笔记-170109
- js 数组的应用