Linux内核 complete()

函数:complete( )

文件包含:

        #include <linux/completion.h>

函数定义:

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

函数定义格式:

void complete(struct completion *)

函数功能描述:

此函数主要用于唤醒等待队列中的睡眠进程,并能记录等待队列被唤醒的次数,唤醒次数保存在参数的done字段中。此函数实现唤醒等待队列中的进程通过调用函数__wake_up_locked( ),传递的参数确定唤醒的进程的状态只能是TASK_INTERRUPTIBLE状态或TASK_UNINTERRUPTIBLE状态,并且唤醒进程不是同步,即只能按等待队列中进程的顺序一个一个的唤醒。如果第一个被唤醒的等待队列中的等待队列元素的f lags字段的值是WQ_FLAG_EXCLUSIVE,则唤醒停止,否则能继续唤醒等待队列中的其他进程。

输入参数说明:

此函数的输入参数是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( )

返回参数说明:

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

实例解析:

编写测试文件: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查看模块插入结果,会出现如图所示的结果。

Linux内核 complete()

结果分析:

由上图可以看出子进程和父进程都执行了,并且子进程在父进程之前执行完毕。在子进程执行时父进程的状态发生了变化,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( )

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!