美文网首页程序员
内核线程同步之completion

内核线程同步之completion

作者: 网路元素 | 来源:发表于2019-10-12 07:01 被阅读0次

Completion,完成量,用于多线程间同步,即线程A要往下执行需要等待线程B执行到指定代码后才继续执行,这时就可以使用该机制,用于一个线程告诉另一个线程指定工作已完成。 在Linux Kernel源码include/linux/completion.h文件里有相关的结构体和接口函数的定义和声明,从结构体可以看到其是基于等待队列机制实现的,该机制后期再了解,下面是completion相关内容:

struct completion {
        unsigned int done;
        wait_queue_head_t wait;
};
static inline void init_completion(struct completion *x) ;
static inline void reinit_completion(struct completion *x);
extern void wait_for_completion(struct completion *);
extern int wait_for_completion_interruptible(struct completion *x);
extern int wait_for_completion_killable(struct completion *x);
extern unsigned long wait_for_completion_timeout(struct completion *x,
        unsigned long timeout);
extern long wait_for_completion_interruptible_timeout(
        struct completion *x, unsigned long timeout);
extern long wait_for_completion_killable_timeout(
        struct completion *x, unsigned long timeout);
extern bool try_wait_for_completion(struct completion *x);
extern bool completion_done(struct completion *x);
extern void complete(struct completion *);
extern void complete_all(struct completion *);

其中结构体中的done相当于一个统计量,当其为0时表示等待队列上还有线程在等待,如果大于0则表示有多少个线程等待的条件已完成,不需等待可继续执行。 

init_completion初始化一个completion结构体,将done设置为0,并初始化wait等待队列头部。而 reinit_completion则是在complete_all执行后completion仍想继续使用时必须执行的函数。 

wait_for_completion则等待创建的completion量完成,即等待complete或complete_done函数执行。而_interruptible则表示该等待可被信号中断,_killable则表示该等待可被kill信号打断,_timeout则表示该等待只等待有限的时间,还有其他的组合则是这3种条件的组成,而try_wait_for_completion则相当于wait_for_completion_timeout中timeout为0的情况。 

completion_done则是个判断条件,返回0表示还有等待者,为1则没有等待者。 

接下来就演示两个内核线程间如何使用completion量,源码如下:

#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/signal.h>

static struct task_struct * slam1, * slam2;
static struct completion slam_comp;

extern unsigned long msleep_interruptible(unsigned int msecs);

static int slam1_func(void *data)
{
        do{
                printk("I'm in slam1,wating slam_comp completion!\n");
                wait_for_completion(&slam_comp);
                printk("slam1:slam_comp completion done!\n");
        }while(!kthread_should_stop());

        return 0;
}

static int slam2_func(void *data)
{
        int i = 0;
        do{
                printk("I'm in slam2_func,i'll do complete!\n");
                i++;
                if(i%2 == 0){
                        complete(&slam_comp);
                        i=0;
                }
                msleep_interruptible(5000);
        }while(!kthread_should_stop());

        return 0;
}

static __init int kthread_completion_init(void)
{
        init_completion(&slam_comp);

        slam1 = kthread_run(slam1_func, NULL, "slam1");
        if(IS_ERR(slam1))
        {
                printk("kthread_run slam1 failed!\n");
                return 1;
        }

        slam2 = kthread_run(slam2_func, NULL, "slam2");
        if(IS_ERR(slam2))
        {
                printk("kthread_run slam2 failed!\n");
                return 1;
        }

        return 0;
}

static __exit void kthread_completion_exit(void)
{
        if(!IS_ERR(slam1))
                kthread_stop(slam1);

        if(!IS_ERR(slam2))
                kthread_stop(slam2);
}

module_init(kthread_completion_init);
module_exit(kthread_completion_exit);

相应的Makefile文件内容如下:

obj-m += kthread_completion_example.o

CUR_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

all:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean

而相应的源码文件目录树如下:

 /home/xinu/xinu/linux_kernel_driver_l1/kthread_completion_example
├── kthread_completion_example.c
└── Makefile 

等我们编译加截后,就可以看到两个内核线程中的打印信息顺序了,这与我们预想的一样吧。但在卸载模块时要等很久才能结束,这个会随着线程数的增多而变得越慢,于是我们将kthread_should_stop改为自加的exit_flag标志,并且在卸载函数中修改exit_flag标志和complete_all完成相应的completion,源码修改后如下:

#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/signal.h>

static struct task_struct * slam1, * slam2;
static struct completion slam_comp;

static int exit_flag = 0;

extern unsigned long msleep_interruptible(unsigned int msecs);

static int slam1_func(void *data)
{
        do{
                printk("I'm in slam1,wating slam_comp completion!\n");
                wait_for_completion(&slam_comp);
                printk("slam1:slam_comp completion done!\n");
        }while(!exit_flag);

        return 0;
}

static int slam2_func(void *data)
{
        int i = 0;
        do{
                printk("I'm in slam2_func,i'll do complete!\n");
                i++;
                if(i%2 == 0){
                        complete(&slam_comp);
                        i=0;
                }
                msleep_interruptible(5000);
        }while(!exit_flag);

        return 0;
}

static __init int kthread_completion_init(void)
{
        init_completion(&slam_comp);

        slam1 = kthread_run(slam1_func, NULL, "slam1");

        if(IS_ERR(slam1))
        {
                printk("kthread_run slam1 failed!\n");
                return 1;
        }

        slam2 = kthread_run(slam2_func, NULL, "slam2");
        if(IS_ERR(slam2))
        {
                printk("kthread_run slam2 failed!\n");
                return 1;
        }

        return 0;
}

static __exit void kthread_completion_exit(void)
{
        exit_flag = 1;
        complete_all(&slam_comp);

        if(!IS_ERR(slam1))
                kthread_stop(slam1);

        if(!IS_ERR(slam2))
                kthread_stop(slam2);
}

module_init(kthread_completion_init);
module_exit(kthread_completion_exit);

修改后的代码在卸载时间就跟以前的模块一样了,很快就可以退出。感受了一遍completion,我们以后就可以在多线程中开始有“秩序”了,因为多个线程到底哪个先哪个慢执行,这取决于操作系统的调度算法。 

参考网址: 

http://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548458.html
http://blog.csdn.net/qb_2008/article/details/6837262
http://silverfoxkkk.pixnet.net/blog/post/44902897-lddp%3A%E5%8D%81%E3%80%81%E5%90%8C%E6%AD%A5%E8%88%87%E9%8E%96%E5%AE%9A

相关文章

  • 内核线程同步之completion

    Completion,完成量,用于多线程间同步,即线程A要往下执行需要等待线程B执行到指定代码后才继续执行,这时就...

  • 内核线程同步之wait_queue

    在《内核线程同步之completion》一文中说到completion完成量也是基于wait_queue等待队列机...

  • 内核线程同步之signal

    对于内核线程,尤其是在同一模块里有多个时,我们想终止其中一个,而又不想卸载模块,此时signal就帮上我们一个大忙...

  • Windows内核模式下的线程同步

    Windwos下内核模式/用户模式实现线程同步的比较 在用户模式下进行线程同步的最大好处就是非常快。利用内核对象进...

  • 同步异步,阻塞和非阻塞概念说明

    同步和异步:就是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续...

  • 深入理解异步I/O+epoll+协程

    前言 同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作...

  • 网络 I/O 模型的演进

    一、同步和异步 同步和异步描述的是用户线程与内核的交互方式: 同步是指用户线程发起 I/O 请求后需要等待或者轮询...

  • 五种 I/O 模型简述

    相关概念 同步和异步 描述的是用户线程与内核的交互方式: 同步是指用户线程发起 I/O 请求后需要等待(阻塞)或者...

  • 浏览器JS运行机制(线程)

    浏览器常驻线程 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:jav...

  • Linux之IO模型机制

    同步和异步 同步和异步是用户线程与内核交互的方式,关注的是消息通知机制,是如何通知调用者. 同步: synchro...

网友评论

    本文标题:内核线程同步之completion

    本文链接:https://www.haomeiwen.com/subject/rvalyctx.html