内核线程

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

    有时候我们需要有个程序来监听和处理某些指定事件,此时经常会做个服务,让其在后台执行,这在应用程序这样的用户态很经常用到,而在Linux Kernel里也会有类似的经历,此时同样可以使用线程来实现,不过它叫内核线程,只能在内核中由其他的线程来创建,而所有的内核线程由kthreadd创建,故而使用ps -ef命令看到所有被[]括起来的内核线程(守护进程)对应的PPID均为2。内核线程与普通用户态线程除了内核线程没有独立地址空间(其mm成员指向NULL)外,其他的可被调度和被抢占均支持。我们还是以实践为主,下面是这次会使用到跟内核线程相关的API函数和宏,在include/linux/kthread.h文件中均有定义: 

    1.kthread_create(),创建内核线程

    #define kthread_create(threadfn, data, namefmt, arg...) \
            kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)

    2.kthread_run(),创建并运行内核线程

    #define kthread_run(threadfn, data, namefmt, ...) \
    ({ \
            struct task_struct *__k \
                    = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
            if (!IS_ERR(__k)) \
                    wake_up_process(__k); \
            __k; \
    })

    看到与kthread_create()的区别了吧,多了个wake_up_process()操作。 

    3.kthread_stop(),停止指定内核线程

    int kthread_stop(struct task_struct *k);

    4.kthread_should_stop(),线程该停止吗?

    bool kthread_should_stop(void);

    5.kthread_bind(),绑定创建好的线程在执行CPU核心上运行

    void kthread_bind(struct task_struct *k, unsigned int cpu);

    也可在创建的时候调用如下函数在创建的同时一起绑定:

    struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                       void *data,
                       unsigned int cpu,
                       const char *namefmt);

    两者在使用后需要调用kthread_run()里用到的wake_up_process()才能进入运行队列。 

    从上面的API看到,关键人物是struct task_struct,其在include/linux/sched.h文件中定义。 

    至此,相关需使用的API已简略说明,而作为线程,对编程来说只是一个函数的实现,该函数一般作为死循环处理,当然在该循环中会进行判断,确认该线程是不是该停止退出了,如果该退出则做相应的处理,如果不退出则继续处理。而作为内核线程,要能主动让出CPU去运行其他线程也必须能重新被调度,这需要调用schedule函数等相关方式,schedule相关函数在kernel/timer.c文件中有定义: 

    1.schedule_timeout(),让CPU调度运行其他线程并等待指定时间后本线程被重新调度,其不改变当前状态

    signed long __sched schedule_timeout(signed long timeout) ;

    2.schedule_timeout_interruptible(),其会改变当前状态为可被中断

    signed long __sched schedule_timeout_interruptible(signed long timeout) ;

    其相当于sleep操作,好了,下面是重头戏了,看看源代码是如何使用这些API的吧:

    #include <linux/module.h>
    #include <linux/kthread.h>
    #include <linux/slab.h>

    static struct task_struct * slam_unbind_thread = NULL;

    static int slam_bind_func(void *data)
    {
            unsigned int cur_cpu = *((unsigned int *)data);
            printk("[slam_bind_thread/%d] start!\n", cur_cpu);

            while(!kthread_should_stop())
            {
                    printk("I'm in [slam_bind_thread/%d]!\n", cur_cpu);
                    schedule_timeout(msecs_to_jiffies(5000));
            }

            printk("[slam_bind_thread/%d] end!\n", cur_cpu);
            return 0;
    }

    static int slam_unbind_func(void *data)
    {
            char *slam_data = kzalloc(strlen(data)+1, GFP_KERNEL);
            strncpy(slam_data, data, strlen(data));

            while(!kthread_should_stop())
            {
                    printk("Unbind Thread:%s(%ld)\n", slam_data, jiffies);
                    schedule_timeout(msecs_to_jiffies(5000));
            }

            kfree(slam_data);
            return 0;
    }

    static __init int kthread_example_init(void)
    {
            int cur_cpu;
            unsigned int cpus = num_online_cpus();
            unsigned int bind_thread_params[cpus];

            struct task_struct *slam_bind_threads[cpus];
            slam_unbind_thread = kthread_run(slam_unbind_func, "slam-xinu", "slam_unbind");

            for_each_present_cpu(cur_cpu)
            {
                    bind_thread_params[cur_cpu] = cur_cpu;
                    slam_bind_threads[cur_cpu] = kthread_create(slam_bind_func, (void *)(bind_thread_params+cur_cpu), "BindThread/%d", cur_cpu);
                    kthread_bind(slam_bind_threads[cur_cpu], cur_cpu);
                    wake_up_process(slam_bind_threads[cur_cpu]);
            }

            schedule_timeout_interruptible(msecs_to_jiffies(30*1000));

             for(cur_cpu=0;cur_cpu<cpus;cur_cpu++)
             {
                  kthread_stop(slam_bind_threads[cur_cpu]);
             }

             return 0;
    }

    static __exit void kthread_example_exit(void)
    {
            if(slam_unbind_thread)
            {
                    kthread_stop(slam_unbind_thread);
            }
    }

    module_init(kthread_example_init);
    module_exit(kthread_example_exit);

    相应的Makefile文件内容如下:

    obj-m += kthread_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_example/
    ├── kthread_example.c
    └── Makefile 

    该驱动测试验证了内核线程,实现了一个标准的未绑定指定CPU的线程,还有多个绑定指定CPU核心的线程。其中,绑定的多个在模块加载30秒内正常运行,模块等完这30秒就停掉这些线程,此时insmod也不再卡住,回到命令行状态,而未绑定的线程仍继续运行,直到rmmod停止。 

    参考网址: 

    http://blog.chinaunix.net/uid-28693738-id-4137973.html
    http://www.bluezd.info/archives/kernel-thread-%E7%9A%84%E5%88%9B%E5%BB%BA
    http://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545624.html

    相关文章

      网友评论

        本文标题:内核线程

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