completion定义
定义的目录:include/linux/completion.h
/*
* struct completion - structure used to maintain state for a "completion"
*
* This is the opaque structure used to maintain the state for a "completion".
* Completions currently use a FIFO to queue threads that have to wait for
* the "completion" event.
*
* See also: complete(), wait_for_completion() (and friends _timeout,
* _interruptible, _interruptible_timeout, and _killable), init_completion(),
* reinit_completion(), and macros DECLARE_COMPLETION(),
* DECLARE_COMPLETION_ONSTACK().
*/
struct completion {
unsigned int done;//指示等待的事件是否完成。初始化时为0。如果为0,则表示等待的事件未完成。大于0表示等待的事件已经完成。
wait_queue_head_t wait;//存放等待该事件完成的进程队列。
};
completion作用
虽然信号量可以用于实现同步,但往往可能会出现一些不好的结果。例如:当进程A分配了一个临时信号量变量,把它初始化为关闭的MUTEX,并把其地址传递给进程B,然后在A之上调用down()
,进程A打算一旦被唤醒就撤销给信号量。随后,运行在不同CPU上的进程B在同一个信号量上调用up()
。然而,up()
和down()
的目前实现还允许这两个函数在同一个信号量上并发。因此,进程A可以被唤醒并撤销临时信号量,而进程B还在运行up()
函数。结果up()
可能试图访问一个不存在的数据结构。这样就会出现错误。为了防止发生这种错误就专门设计了completion机制专门用于同步。
completion操作
定义和初始化:
struct completion completion;
init_completion(&completion);
直接定义并调用init_completion()
初始化。init_completion()
会将done字段初始化为0,wait字段的自旋锁为未锁,等待队列为空。这说明调用该完成量的进程必须等待某事件完成(即另外一进程必须先调用completiom()
唤醒该完成量)。
/**
* init_completion - Initialize a dynamically allocated completion
* @x: pointer to 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);
}
DECLARE_COMPLETION(completion);
直接定义并初始化completion完成量,效果等同于以上定义方式。
/**
* DECLARE_COMPLETION - declare and initialize a completion structure
* @work: identifier for the completion structure
*
* This macro declares and initializes a completion structure. Generally used
* for static declarations. You should use the _ONSTACK variant for automatic
* variables.
*/
#define DECLARE_COMPLETION(work) \
struct completion work = COMPLETION_INITIALIZER(work)
等待完成量:
wait_for_completion()
函数,该函数定义在kernel/sched/completion.c中,其主要通过调用do_wait_for_common
函数来实现,内容如下:
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);
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;
}
该函数相当于信号量中的down()
操作。不过在操作中对使用其自身的自旋锁。如果done为0,则说明等待的事件没有完成,则调用DECLARE_WAITQUEUE()
定义等待队列wait并将当前进程添加进等待队列wait。然后将wait添加进该完成量的等待队列的末尾,进入循环。设置当前进程为不可中断状态(TASK_UNINTERRUPTIBLE
),释放自旋锁并让当前进程进入睡眠状态。一旦进程被调度唤醒据又获得自旋锁并查看等待的事件是否完成。如果完成(大于0),则从完成量的等待队列中删除等待的进程,并自减done ,释放自旋锁,从等待状态返回继续运行。否则继续睡眠。如果done于大0,则说明等待的事件已经完成,则自减done,直接返回
wait_for_completion_timeout()
函数,该函数定义在kernel/sched/completion.c,其主要通过调用do_wait_for_common
函数来实现,也是等待完成量。与wait_for_completion()
最大的区别是它等待超时的情况下返回。也就是说如果经过给定的时间该完成量还没有被唤醒,就直接返回。这样最大的好处是经过一定的时间该进程已经不需要等待某事件,那么就可以直接被唤醒继续执行
wait_for_completion_interruptible()
函数,该函数定义kernel/sched/completion.c中
wait_for_completion_interruptible_timeout()
函数,该函数定义在kernel/sched/completion.c中
唤醒完成量
complete()
函数,用于唤醒一个等待该完成量的进程。该函数定义在kernel/sched/completion.c中,内容如下:
/**
* 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_locked(&x->wait, TASK_NORMAL, 1);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
complete_all()
函数,唤醒所有等待给完成量的进程。该函数定义在kernel/sched/completion.c中,内容如下:
/**
* complete_all: - signals all threads waiting on this completion
* @x: holds the state of this particular completion
*
* This will wake up all threads waiting on this particular completion event.
*
* 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_all(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done += UINT_MAX/2;
__wake_up_locked(&x->wait, TASK_NORMAL, 0);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
例子程序:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
//这三个头文件与内核线程的使用有关;
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
//完成量相关
#include <linux/completion.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("*************");
MODULE_VERSION("4.7.0.000");
static int sleep_time = (1*10*HZ);
static int shared_res = 0;
//STEP1:定义完成量
struct completion my_comp;
//STEP5:实现线程函数
static int thread_process1(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
wait_for_completion(&my_comp);
shared_res++;
//STEP4:对临界区解锁
complete(&my_comp);
mdelay(sleep_time);
}
return 12;
};
static int thread_process2(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
wait_for_completion(&my_comp);
shared_res++;
//STEP4:对临界区解锁
complete(&my_comp);
msleep(sleep_time);
}
return 34;
};
static int thread_process3(void* param)
{
int val = 0;//, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
wait_for_completion(&my_comp);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
//STEP4:对临界区解锁
complete(&my_comp);
msleep(sleep_time);
}
return 56;
};
static int thread_process4(void* param)
{
int val = 0;//, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
wait_for_completion(&my_comp);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
//STEP4:对临界区解锁
complete(&my_comp);
msleep(sleep_time);
}
return 78;
};
static struct task_struct* my_thread1 = NULL;
static struct task_struct* my_thread2 = NULL;
static struct task_struct* my_thread3 = NULL;
static struct task_struct* my_thread4 = NULL;
static int __init study_init(void)
{
int err = 0;
printk("%s\n", __PRETTY_FUNCTION__);
//STEP2:初始化完成量
init_completion(&my_comp);
printk("init completion ok\n");
complete_all(&my_comp);
my_thread1 = kthread_create(thread_process1, NULL, "my_thread1");
if(IS_ERR(my_thread1))
{
err = PTR_ERR(my_thread1);
my_thread1 = NULL;
printk(KERN_ERR "unable to start kernel thread1:%d\n", err);
return err;
}
my_thread2 = kthread_create(thread_process2, NULL, "my_thread2");
if(IS_ERR(my_thread2))
{
err = PTR_ERR(my_thread2);
my_thread2 = NULL;
printk(KERN_ERR "unable to start kernel thread2:%d\n", err);
return err;
}
my_thread3 = kthread_create(thread_process3, NULL, "my_thread3");
if(IS_ERR(my_thread3))
{
err = PTR_ERR(my_thread3);
my_thread3 = NULL;
printk(KERN_ERR "unable to start kernel thread3:%d\n", err);
return err;
}
my_thread4 = kthread_create(thread_process4, NULL, "my_thread4");
if(IS_ERR(my_thread4))
{
err = PTR_ERR(my_thread4);
my_thread4 = NULL;
printk(KERN_ERR "unable to start kernel thread4:%d\n", err);
return err;
}
wake_up_process(my_thread1);
wake_up_process(my_thread2);
wake_up_process(my_thread3);
wake_up_process(my_thread4);
printk("%s:all kernel thread start;\n", __FUNCTION__);
return 0;
}
static void __exit study_exit(void)
{
int ret = -1;
printk("%s\n",__PRETTY_FUNCTION__);
//complete_all(&my_comp);
complete(&my_comp);
if(my_thread1)
{
ret = kthread_stop(my_thread1);
my_thread1 = NULL;
printk("kernel thread1 stop,exit code is %d;\n",ret);
}
//complete_all(&my_comp);
complete(&my_comp);
if(my_thread2)
{
ret = kthread_stop(my_thread2);
my_thread2 = NULL;
printk("kernel thread2 stop,exit code is %d;\n",ret);
}
//complete_all(&my_comp);
complete(&my_comp);
if(my_thread3)
{
ret = kthread_stop(my_thread3);
my_thread3 = NULL;
printk("kernel thread3 stop,exit code is %d;\n",ret);
}
//complete_all(&my_comp);
complete(&my_comp);
if(my_thread4)
{
ret = kthread_stop(my_thread4);
my_thread4 = NULL;
printk("kernel thread4 stop,exit code is %d;\n",ret);
}
printk("%s:all kernel thread stop;\n", __FUNCTION__);
}
module_init(study_init);
module_exit(study_exit);
未完待续。。。。