Linux内核API try_wait_for_completion

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所示的结果,终端停止在此处,不会重新回到命令行模式。

Linux内核API try_wait_for_completion

重新打开一个终端,输入命令dmesg -c查看函数运行结果,会出现如图B所示的结果。

Linux内核API try_wait_for_completion

然后输入命令lsmod | head -6查看内核现有模块,会出现如图C所示的结果。

Linux内核API try_wait_for_completion

重启系统,注释掉子进程处理函数中的第一处“complete(&comple); ”,去掉第二处“complete(&comple); ”的注释;然后保存文件,重新编译、加载模块,输入命令dmesg -c会出现如图D所示的结果。

Linux内核API try_wait_for_completion

结果分析:

对于图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的值,使循环结束,父进程阻塞能够被解除,继续执行完毕。

赞(1)
未经允许不得转载:极客笔记 » Linux内核API try_wait_for_completion
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址