try_wait_for_completion函数功能描述:此函数用于尝试无阻塞的消耗一个completion,与函数wait_for_completion( )不同,wait_for_completion( )是有阻塞的。如果传入的参数x的done字段的值为0,此次尝试失败;如果传入的参数的x的done字段的值为1,此次尝试成功,并且字段done的值将减少1。简单点说此函数用于减少参数x的done字段的值,如果done字段的值大于0,则减1,否则不变。
try_wait_for_completion文件包含
#include <linux/completion.h>
try_wait_for_completion函数定义
在内核源码中的位置:linux-3.19.3/kernel/sched/completion.c
函数定义格式:
bool try_wait_for_completion(struct completion *x)
try_wait_for_completion输入参数说明
此函数的输入参数是struct completion结构体类型的指针,包含一个等待队列信息及等待队列的状态信息。等待队列的状态代表此等待队列是否被唤醒过,其定义及详细解释参考函数complete( )输入参数说明部分。
try_wait_for_completion返回参数说明
此函数的返回结果是bool类型的变量,结果可能的取值是0和1,返回0说明当前completion变量的done字段的值为0;返回1说明当前completion变量的done字段的值大于0,并且此时字段done的值减少1。
try_wait_for_completion实例解析
编写测试文件:try_wait_for_completion.c
头文件引用及全局变量定义:
/*头文件引用*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/kthread.h>
MODULE_LICENSE("GPL");
/*全局变量定义*/
static struct completion comple; //用于保存completion的状态
struct task_struct * old_thread; //保存初始化进程的信息
子进程处理函数定义:
int my_function(void * argc)
{
bool done;
wait_queue_head_t head;
wait_queue_t data;
printk("in the kernel thread function! \n");
init_waitqueue_head(&head); //初始化等待队列头元素
init_waitqueue_entry(&data, current); //用当前进程初始化等待队列元素
add_wait_queue(&head, &data); //将当前进程插入到等待队列中
schedule_timeout_uninterruptible(10); //将等待队列置于不可中断的等待状态
complete(&comple); //调用函数唤醒进程,并更改done字段的值
printk("the value of done of the comple is:%d\n", comple.done); //显示字段done的值
done=try_wait_for_completion(&comple);
// 尝试无阻塞的消耗一个completion,即减少done字段的值
printk("the value of done of the comple is:%d\n", comple.done);
// 显示函数调用之后字段done的值
printk("the return result of the try_wait_for_completion is:%d\n", done);
// 显示函数返回结果
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
printk("the state of the parent is:%ld\n", old_thread->state); //显示父进程的状态
// complete(&comple);
printk("out the kernel thread function\n");
return 0;
}
模块加载函数定义:
static int __init try_wait_for_completion_init(void)
{
struct task_struct * result;
wait_queue_t data;
printk("into try_wait_for_completion_init.\n");
old_thread = current;
result=kthread_create_on_node(my_function, NULL, -1, "try_wait_for_completion");
//创建新进程
wake_up_process(result);
init_completion(&comple); //初始化completion变量
init_waitqueue_entry(&data, result); //用新进程初始化等待队列元素
__add_wait_queue_tail(&(comple.wait), &data); //将新进程加入等待队列的尾部
wait_for_completion(&comple); //阻塞进程,等待新进程的结束
printk("the pid of new thread is :%d\n", result->pid);
//显示kthread_create_on_node( )函数的返回结果
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
printk("out try_wait_for_completion_init.\n");
return 0;
}
模块退出函数定义:
static void __exit try_wait_for_completion_exit(void)
{
printk("Goodbye try_wait_for_completion\n");
}
模块加载、退出函数调用:
module_init(try_wait_for_completion_init);
module_exit(try_wait_for_completion_exit);
实例运行结果及分析:
首先编译模块,执行命令insmod try_wait_for_completion.ko插入内核模块,此时会出现如图A
所示的结果,终端停止在此处,不会重新回到命令行模式。
重新打开一个终端,输入命令dmesg -c查看函数运行结果,会出现如图B
所示的结果。
然后输入命令lsmod | head -6查看内核现有模块,会出现如图C
所示的结果。
重启系统,注释掉子进程处理函数中的第一处“complete(&comple); ”
,去掉第二处“complete(&comple); ”
的注释;然后保存文件,重新编译、加载模块,输入命令dmesg -c会出现如图D
所示的结果。
结果分析:
对于图A
、图B
和图C
的测试程序,函数complete( )的执行在函数try_wait_for_completion( )之前,所以当函数执行时输入参数的done字段的值为1,此时尝试消耗completion会成功,由图B
的输出结果也可以判断这一点,并且在函数try_wait_for_completion( )执行前后输入参数的done字段的值减少了1。在子进程中显示的父进程的状态为0,即处于TASK_RUNNING状态,这是由于函数complete( )唤醒等待队列的缘故,此次测试程序阻塞是因为函数wait_for_completion( )内部有个循环,函数complete( )执行之后,循环本可以结束的,但函数try_wait_for_completion( )将字段done的值减为0,导致循环无法结束,所以出现图A
的结果。循环无法结束,模块将一直存在于内核中,所以当使用命令lsmod命令查看内核模块时,会出现图C
所示的结果。
对于图D
的测试程序,函数complete( )的执行在函数try_wait_for_completion( )执行之后,当函数try_wait_for_completion( )执行之时,输入参数的字段done的值为0,所以此次尝试消耗completion会失败,由图“D的输出结果可以判断这一点。此次测试程序没有出现第一次测试程序一样的阻塞现象,因为函数complete( )唤醒了等待队列,改变了字段done的值,使循环结束,父进程阻塞能够被解除,继续执行完毕。