上次讲解了一下add_wait_queue和add_wait_queue_exclusive两个函数,今天来讲另一个函数complete函数。
文件包含:
#include<linux/completion.h>
这个函数也是与内核进程调度相关的一个函数,因此它也位于kernel/sched/目录下,具体的实现在completion.c中,我们可以看看它的具体实现,这样对于我们更好的理解它的工作原理会很有帮助。
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函数调用了__wake_up_locked函数,只是在调用时采用了自旋锁并且关闭了中断。
函数功能:
complete函数用于唤醒等待队列中的睡眠进程,并记录等待队列中的唤醒次数,保存在字段done中。函数的参数是completion的一个结构体。
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
completion结构体除了done字段之外,就是一个wait_queue_head_t的等待队列头。需要注意的是能唤醒的进程状态只能是TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE,并且唤醒进程不是同步的,就是说只能唤醒一个然后再去唤醒另一个。
实例解析:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/sched.h>
#include<linux/wait.h>
#include<linux/completion.h>
#include<linux/kthread.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("viz.xu, xujiweigo@163.com");
static struct completion comple;
static struct task_struct* old_task;
int my_test_function(void* args)
{
printk("enter kernel function!n");
printk("the current pid is %dn",current->pid);
printk("the value of done of the comple:%dn",comple.done);
printk("the state of the init thread is %ldn",old_task->state);
complete(&comple);
printk("the value of done of the comple:%dn",comple.done);
printk("the state of the init thread is %ldn",old_task->state);
return 0;
}
int __init complete_init(void)
{
printk("complete init! current->pid = %dn",current->pid);
struct task_struct* new_task = NULL;
old_task = current;
new_task = kthread_create_on_node(my_test_function,NULL,-1,"comple");
init_completion(&comple);
wait_queue_entry_t data;
init_waitqueue_entry(&data,current);
wake_up_process(new_task);
add_wait_queue(&(comple.wait),&data);
long left_time = schedule_timeout_uninterruptible(1000);
printk("the pid of new_task is %dn",new_task->pid);
printk("the return result of the schedule_timeout_uninterruptible is %dn",left_time);
return 0;
}
void __exit complete_exit(void)
{
printk("complete exit!n");
return;
}
module_init(complete_init);
module_exit(complete_exit);
结果分析:
[ 2814.212263] complete init! current->pid = 40020
[ 2814.212397] enter kernel function!
[ 2814.212399] the current pid is 40021
[ 2814.212400] the value of done of the comple:0
[ 2814.212401] the state of the init thread is 2
[ 2814.212403] the value of done of the comple:1
[ 2814.212404] the state of the init thread is 0
[ 2814.212406] the pid of new_task is 40021
[ 2814.212407] the return result of the schedule_timeout_uninterruptible is 1000
从结果可以看出,子进程和父进程都执行了,并且子进程在父进程之前执行完毕。在my_test_ function中调用complete函数,父进程的状态发生了改变2->0,这是因为我们在init函数中调用了schedule_timeout_uninterruptible,使得父进程处于TASK_UNINTERRUPTIBLE状态。同是,调用complete函数之后,comple的done字段变为1,说明参数在等待队列被唤醒了一次。