Linux内核 complete_all()

函数:complete_all( )

文件包含:

          #include <linux/completion.h>

函数定义:

在内核源码中的位置:linux-3.19.3/kernel/sched/completion.c

函数定义格式:

void complete_all(struct completion *)

函数功能描述:

此函数主要用于唤醒等待队列中的所有的睡眠进程,并能更改等待队列被唤醒的次数,唤醒次数保存在参数的done字段中,函数设置字段done的值为在done原值的基础上加上UINT_MAX的二分之一,其中UINT_MAX内核的定义值为:#def ine UINT_MAX(~0U),值为4294967295。此函数实现唤醒等待队列中的进程通过调用函数__wake_up_locked( ),传递的参数确定唤醒的进程的状态只能是TASK_INTERRUPTIBLE状态或TASK_UNINTERRUPTIBLE状态,并且唤醒进程不是同步的,即只能按等待队列中进程的顺序一个一个地唤醒,但能唤醒等待队列中的所有睡眠状态的进程。

输入参数说明:

此函数的输入参数是struct completion结构体类型的指针,包含一个等待队列信息及等待队列的状态信息,等待队列的状态代表此等待队列是否被唤醒过,其定义及详细解释参考函数complete( )

返回参数说明:

此函数的返回结果是void类型的变量,即不返回任何类型的结果。

实例解析:

编写测试文件:complete_all.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_all(&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_all_init(void)
{
    char namefrm[] = "complete_all";
    struct task_struct * result;
    long left_time;
    wait_queue_t data;
    printk("into complete_all_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 return result of the schedule_timeout_uninterruptible is:%ld\n", left_time);          //显示函数sleep_on_timeout( )的返回结果
    printk("the pid of result is :%d\n", result->pid);
                             //显示函数kernel_thread( )的返回结果
    printk("the current pid is:%d\n", current->pid);  //显示当前进程的PID值
    printk("out complete_all_init.\n");
    return 0;
}

模块退出函数定义:

static void __exit complete_all_exit(void)
{
    printk("Goodbye complete_all\n");
}

模块加载、退出函数调用:

module_init(complete_all_init);
module_exit(complete_all_exit);

实例运行结果及分析:

首先编译模块,执行命令insmod complete_all.ko插入内核模块,然后输入命令dmesg -c查看模块插入结果,会出现如图所示的结果。

Linux内核 complete_all()

结果分析:

由图上4结果可以看出子进程和父进程都已执行,并且子进程在父进程之前执行完毕。在子进程执行时父进程的状态发生了变化,state值由2变为0。状态值首先为2是因为函数schedule_timeout_uninterruptible( )执行的原因,此函数使父进程处于TASK_UNINTERRUPTIBLE状态。在子进程中,函数complete_all( )调用之前父进程的状态值为2,参数comple的done字段的值为0,函数调用之后,父进程的状态变为0,处于TASK_RUNNING状态,参数comple的done字段的值变为2147483647,说明函数complete_all( )能够改变done字段的值,并且父进程被成功唤醒。函数schedule_timeout_uninterruptible( )的返回结果是1000,说明父进程是被强制唤醒的,而不是等待超时才被唤醒的,这正好也说明了函数complete_all( )能够唤醒等待队列中的进程。

函数功能比较:

函数complete( )和函数complete_all( )都能唤醒等待队列中的睡眠进程,但在唤醒数量上可能有差别。函数complete( )调用函数__wake_up_locked( )时传递的第三个参数值是1,而函数complete_all( )函数传递的相应的参数值是0,从而保证函数complete_all( )能够唤醒等待队列中的所有的进程,而函数complete( )有可能不能。当等待队列中的第一个等待队列元素的f lags字段的值是WQ_FLAG_EXCLUSIVE时,函数complete( )将只唤醒此等待队列元素对应的进程,然后退出,而函数complete_all( )将继续唤醒其他的进程。

函数complete( )调用时,只是将参数的done字段加1,这个字段可以代表等待队列被唤醒的次数,而函数complete_all( )在调用时,将参数的done字段的值加上UINT_MAX的二分之一,此时字段done的值就不能代表等待队列被唤醒的次数了。

进程状态说明:

对于进程能够处于的状态,参考函数__wake_up( )

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!