美文网首页程序员
内核线程之User-Mode Helpers

内核线程之User-Mode Helpers

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

    这次学习下如何在Linux内核态执行用户态程序,这就要用到User-Mode Helpers,为什么要这么“逆操作”呢?有些与平常用户态系统调用内核态反着来,其实在U盘热插拔时,就需要用到该功能了,当U盘插入时,驱动识别到U盘设备,最终需要调用用户态的程序和设定好的规则来将其挂载起来,还有其他的应用场景也需要这样的操作,自己好好探索下吧。接下来说说关于User-Mode Helpers,下面是相关的函数(在kernel/kmod.c文件中有定义,下面只给出函数头和函数体较少的代码): 

    1.call_usermodehelper_setup

    struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
            char **envp, gfp_t gfp_mask,
            int (*init)(struct subprocess_info *info, struct cred *new),
            void (*cleanup)(struct subprocess_info *info),
            void *data) ;

    2.call_usermodehelper_exec

    int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) ;

    3.call_usermodehelper

    int call_usermodehelper(char *path, char **argv, char **envp, int wait)
    {
            struct subprocess_info *info;
            gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
            info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
                    NULL, NULL, NULL);
            if (info == NULL)
                    return -ENOMEM;
            return call_usermodehelper_exec(info, wait);
    }

    从上面这3个函数可以看到,第3个最终是调用第1和第2这两个函数的,故而一般直接使用第3个函数就可以了,从上面的代码中有 UMH_NO_WAIT宏,其在include/linux/kmod.h文件中有如下定义:

    #define UMH_NO_WAIT 0 /* don't wait at all */
    #define UMH_WAIT_EXEC 1 /* wait for the exec, but not the process */
    #define UMH_WAIT_PROC 2 /* wait for the process to complete */
    #define UMH_KILLABLE 4 /* wait for EXEC/PROC killable */

    即最终调用 call_usermodehelper_exec函数时所采用的等待策略,宏后面均有注释。而 GFP_ATOMIC和 GFP_KERNEL则是在调用call_usermodehelper_setup函数时所采取的内存分配策略,GFP_ATOMIC是原子性的,不可等待,而GFP_KERNEL则可等待,其在include/linux/gfp.h文件中有相应的宏定义。 

    在上面3个函数中,我们看到使用了 subprocess_info结构体进行牵线搭桥,该结构体在include/linux/kmod.h文件中有如下定义:

    struct subprocess_info {
            struct work_struct work;
            struct completion *complete;
            char *path;
            char **argv;
            char **envp;
            int wait;
            int retval;
            int (*init)(struct subprocess_info *info, struct cred *new);
            void (*cleanup)(struct subprocess_info *info);
            void *data;
    };

    接下来上实例源码吧,本次为了图方便,还是在内核线程的基础上使用UMH(User-Mode Helpers),包含参数的组成形式等,以及完善《内核线程同步之signal》一文中如何在module_exit退出时发信号让线程结束的处理方法:

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

    static struct task_struct * slam_thread = NULL;
    static int run_umh_app(void)
    {
            char *argv[] = {"/bin/touch", "/home/xinu/slam.txt", NULL};
            static char *envp[] = {
                    "HOME=/",
                    "TERM=linux",
                    "PATH=/sbin:/bin:/usr/bin:/usr/sbin",
                    NULL
            };

            return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
    }

    static int slam_func(void *data)
    {
            printk("<xinu>%s()!\n", __func__);
            allow_signal(SIGKILL);
            mdelay(1000);

            while(!signal_pending(current))
            {
                    /* run user-mode helpers */
                    run_umh_app();
                    printk("<xinu>jiffies(%lu)\n", jiffies);
                    set_current_state(TASK_INTERRUPTIBLE);
                    schedule_timeout(msecs_to_jiffies(5000));
            }

            printk("<xinu>leave slam_func!\n");
            return 0;
    }

    static __init int kthread_signal_example_init(void)
    {
            slam_thread = kthread_run(slam_func, NULL, "slam");
            printk("<xinu>kthread_signal_example_init()!\n");
            return 0;
    }

    static __exit void kthread_signal_example_exit(void)
    {
            if(!IS_ERR(slam_thread))
            {
                    send_sig(SIGKILL, slam_thread, 1);
            }
            printk("<xinu>%s()!\n", __FUNCTION__);
    }

    module_init(kthread_signal_example_init);
    module_exit(kthread_signal_example_exit);

    相应的Makefile文件内容如下:

    obj-m += kthread_umh_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_user_mode_helpers_example/
    ├── kthread_umh_example.c
    └── Makefile 

    看到源码里作为argv和envp的最后一个元素必须为NULL,还有用到send_sig函数(在kernel/signal.c文件里有定义)去发SIGKILL信号给对应的线程吧,还有注意 run_umh_app函数的调用位置,现在是放在循环体首(“{”之后),如果是在尾(“}”之前),则在rmmod时会RIP错误。加载模块后,我们会看到/home/xinu目录下slam.txt文件的修改时间不断刷新(如果不存在该文件则会自动创建,调用了touch命令)。 

    至此,对内核信号机制的使用作了补充,了解了如何在内核里发结束信号给指定线程,还有学习了如何在内核态执行用户态程序。 

    参考网址: 

    http://www.linux-mag.com/id/2195/
    http://reneeciou.blogspot.com/2013/08/linux-kernel-threads.html
    http://www.ibm.com/developerworks/cn/linux/l-user-space-apps/

    相关文章

      网友评论

        本文标题:内核线程之User-Mode Helpers

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