complete函数功能描述:此函数主要用于唤醒等待队列中的睡眠进程,并能记录等待队列被唤醒的次数,唤醒次数保存在参数的done字段中。此函数实现唤醒等待队列中的进程通过调用函数__wake_up_locked( ),传递的参数确定唤醒的进程的状态只能是TASK_INTERRUPTIBLE状态或TASK_UNINTERRUPTIBLE状态,并且唤醒进程不是同步,即只能按等待队列中进程的顺序一个一个的唤醒。如果第一个被唤醒的等待队列中的等待队列元素的f lags字段的值是WQ_FLAG_EXCLUSIVE,则唤醒停止,否则能继续唤醒等待队列中的其他进程。
complete文件包含
#include <linux/completion.h>
complete函数定义
在内核源码中的位置:linux-3.19.3/kernel/sched/completion.c
函数定义格式:
void complete(struct completion *)
complete输入参数说明
此函数的输入参数是struct completion结构体类型的指针,包含一个等待队列信息及等待队列的状态信息,等待队列的状态代表此等待队列是否被唤醒过,结构体的定义见文件linux-3.19.3/include/linux/completion.h,如下:
struct completion {
unsigned int done; //保存等待队列的唤醒情况
wait_queue_head_t wait; //等待队列的头元素
};
其中:
- 字段
done
的值是一个无符号的整型变量,初始值是0,当值为0时代表等待队列没有被唤醒过,如果值为非0,表示等待队列被唤醒过,其值代表唤醒的次数。 - 字段
wait
是wait_queue_head_t类型的变量,代表等待队列的头元素,其定义及详细信息参考数__wake_up( )。
complete返回参数说明
此函数的返回结果是void类型的变量,即不返回任何类型的结果。
complete实例解析
编写测试文件:complete.c
头文件引用及全局变量定义:
/*头文件引用*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/wait.h>
#include <linux/completion.h>
#include <linux/kthread.h>
MODULE_LICENSE("GPL");
/*全局变量定义*/
static struct completion comple; //用于保存completion的状态
struct task_struct * old_thread; //用于保存模块初始化进程
子进程处理函数定义:
int my_function(void * argc)
{
printk("in the kernel thread function! \n");
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
printk("the value of done of the comple:%d\n", comple.done); //显示字段done的值
printk("the state of the init function is :%ld\n", old_thread->state);
// 显示父进程的状态
complete(&comple); //调用函数唤醒进程,并更改done字段的值
printk("the value of done of the comple:%d\n", comple.done);
// 显示函数调用之后字段done的值
printk("the state of the init function is :%ld\n", old_thread->state);
// 显示父进程的状态
printk("out the kernel thread function\n");
return 0;
}
模块加载函数定义:
static int __init complete_init(void)
{
char namefrm[] = "complete.c";
struct task_struct * result;
long left_time;
wait_queue_t data;
printk("into complete_init.\n");
old_thread = current;
result=kthread_create_on_node(my_function, NULL, -1, namefrm); //创建新进程
wake_up_process(result);
init_completion(&comple); //初始化全局变量
init_waitqueue_entry(&data, current); //用当前进程初始化等待队列元素
add_wait_queue(&(comple.wait), &data); // 将当前进程加入等待队列中
left_time = schedule_timeout_uninterruptible(1000); //使等待队列进程不可中断的等待状态
printk("the pid of result is :%d\n", result->pid);
//显示函数kernel_thread( )的返回结果
printk("the return result of the schedule_timeout_uninterruptible is:%ld\n", left_time); //显示函数sleep_on_timeout( )的返回结果
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
printk("out complete_init.\n");
return 0;
}
模块退出函数定义:
static void __exit complete_exit(void)
{
printk("Goodbye complete\n");
}
模块加载、退出函数调用:
module_init(complete_init);
module_exit(complete_exit);
实例运行结果及分析:
首先编译模块,执行命令insmod complete.ko插入内核模块,然后输入命令dmesg -c查看模块插入结果,会出现如图所示的结果。
结果分析:
由上图可以看出子进程和父进程都执行了,并且子进程在父进程之前执行完毕。在子进程执行时父进程的状态发生了变化,state值由2变为0,状态值首先为2是因为当函数schedule_timeout_uninterruptible ( )执行时使父进程处于TASK_UNINTERRUPTIBLE状态。在子进程中,函数complete( )调用之前父进程的状态值为2,参数comple的done字段的值为0,函数调用之后,父进程的状态变为0,处于TASK_RUNNING状态,参数comple的done字段的值变为1,说明参数中的等待队列被唤醒一次,并且父进程被成功唤醒。函数schedule_timeout_uninterruptible( )的返回结果是1000,说明父进程是被强制唤醒的而不是等待超时才被唤醒的,这正好也说明了函数complete( )能够唤醒等待队列中的进程。
进程状态说明:对于进程能够处于的状态,参考__wake_up( )。