免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2631 | 回复: 0
打印 上一主题 下一主题

如何在kernel里调用应用层函数 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-25 22:51 |只看该作者 |倒序浏览

               
       
       
       
       
       
       
1. 简介
一般来说,应用层程序的执行是依赖与内核提供的接口,比如内核系统调用来获得系统的时间片,从而获得执行。如下是在Solaris里面的进程模型,我想在Linux里面应该也是差不多的。

       
       
       
       
       
       
但是,能否反其道而行之呢?也就是说,能不能从内核里面的模块直接调用应用层的函数,并且得到执行呢?我们都知道,在应用层编程有很多好处,比如,可以利用现成的代码,有好的调试工具,等等很多。如果可以把一部分程序从内核空间移到用户空间,应该对开发有很大方便。
       
       
       
       
       
       
2. 原理
我编写了一个实验程序,可以从内核空间调用应用层的程序,该程序在最新的Solaris系统实验过。具体方法是利用Solaris提供的door(RPC调用)来完成。我们都知道door是Solaris首创并且进入POSIX标准,可以用于在用户空间内不同进程间的通信。主要结构就是一个Server端用door_create建立一个服务程序,并且和一个文件进行绑定(fattach),这个程序就叫门(door)文件。客户端程序可以通过这个门文件来请求另外一个进程的服务(通过door_call)。
上述流程只是在用户空间内使用。但是除了用户空间,Solaris还提供了一套在kernel空间使用的函数。它们是
       
       
       
       
               
                        步骤
               
               
                        Server端(User
                        Layer)
               
               
                        Client端(Kernel
                        Layer)
               
       
       
               
                        1
               
               
                        door_create
               
               
                       
                       
               
       
       
               
                        2
               
               
                        fattach
               
               
                       
                       
               
       
       
               
                        3
               
               
                       
                       
               
               
                        door_kl_call
               
       
       
               
                        4
               
               
                        door_retur
               
               
                       
                       
               
       
       
       
       
       
       
       
3. 扩展
       
       
       
       
       
       
  • 利用door,很容易在用户空间内写一个daemon进程,用于向内核模块提供服务,在用户空间完成一些操作。这个daemon中最直接的可以应用在log功能上,也就是说,我们可以利用这个daemon给内核模块提供接口,将内核空间的一些信息直接dump出来,写到我们自己的log里面去。当然,还有其他功能可以扩展,就看设计者想用它干什么了!
                   
  • 同样,我们可以把这个体系反过来理解。在内核里面提供一个服务,在用户空间的程序通过door_call来直接调用内核的程序,在内核里面完成相应的功能。作者现在没有编写相应的程序来印证这一点,但是,这应该很容易。就是把上面的程序结构反过来应用就可以了。
           
    4. 具体程序
           
           
           
           
           
           
    下面的程序在最新的Solaris下面运行过。因为不清楚Linux内核是否提供了相应的功能,没有在Linux平台上实验。具体程序请见下面。值得注意的是,这个程序只是一个示例程序,它只是证明了从内核空间是可以调用用户空间函数的,并没有特别的意义。但是它是一个可以扩展的架构,我们可以在这个体系下充分的完成其他功能。
    运行示例:
    编译下面的两个程序,在应用层空间的生成apptool的文件,同时生成Solaris的kernel module--testmod.
    # ./apptool testmod # modload ./testmod
    这时,可以看到,在apptool所在的tty上打印了
    "We provide service!!",这证明了我的应用层服务程序已经被刚才的testmod在内核空间里面调用了! Great!!!
         具体程序如下:
    //应用层程序apptool.c
    #include ....
    #define    SYMLINK_DIR    "/var/run"
    #define    DOORPATH_FMT    "/var/run/%s.upcall" //门文件
    //app给kernel提供的服务类型,目前只写了一个服务函数
    typedef enum kmod_opcode {
        /* Service calls type*/
        KMOD_OP,
            ......
    } kmod_opcode_t;
    /* make sure cleanup 完成了*/
    #pragma fini(local_cleanup)
    static int    error(char *format, ...);
    static int    create_call_door();
    static void    call_handler(void *cookie, char *argp, size_t arg_size,
        door_desc_t *dp, uint_t n_desc);
    static void    handle_op(char *argp, size_t arg_size);
    static int    make_symlink(const char *mod, const char *moddir_suffix,
        char *fullbuf, char *symlinkbuf);
    static void    local_cleanup();
    static char    kmodpath[PATH_MAX + 1] = "";
    static char    symlinkpath[PATH_MAX + 1] = "";
    #ifdef MODDIR_SUFFIX_64
    //64bit的symbolink实现
    static char    kmodpath64[PATH_MAX + 1] = "";
    static char    symlinkpath64[PATH_MAX + 1] = "";
    #endif
    //门调用文件
    static char    *upcall_file = NULL;
    //门调用句柄
    static int    upcall_dfd = -1;
    static int    kmod_id = -1;
    static mutex_t    kmod_init_mutex = DEFAULTMUTEX;
    static cond_t    kmod_init_cv = DEFAULTCV;
    static int    kmod_init = 0;
    //主函数等待
    void main_server_provider() {
        /* simple service provider, loop and pause */
        for ( ; ; )
            pause();
    }
    //创建一个符号连接
    int make_symlink(const char *mod, const char *moddir_suffix,
        char *fullbuf, char *symlinkbuf)
    {
        char    temp[PATH_MAX + 1];
        char    *modname, *lastslash;
        int    pid_ndigits;
        int    symlink_len, name_len;
        char    dummy[1];
        if (moddir_suffix == NULL)
            moddir_suffix = "";
        //得到 mod path string
        if (lastslash = strrchr(mod, '/')) {
            strlcpy(temp, mod, (lastslash - mod + 2));
            strcat(temp, moddir_suffix);
            strcat(temp, lastslash + 1);
        } else {
            sprintf(temp, "%s%s", moddir_suffix, mod);
        }
        //对symbolink的名字是装载的module
        if (realpath(temp, fullbuf) == NULL) {
            error("ERROR: make_symlink: realpath(%s, fullbuf): %s\n",
                temp, strerror(errno));
            return (-1);
        }
        modname = strrchr(fullbuf, '/') + 1;
        //计算PID长度
        pid_ndigits = snprintf(dummy, sizeof (dummy), "%d", getpid());
       
            //在SYMLINK_DIR里面创建符号连接
        name_len = (MODMAXNAMELEN - 1) - (pid_ndigits + 1);
        symlink_len = sprintf(symlinkbuf, "%s/%s%.*s.%d",
            SYMLINK_DIR, moddir_suffix, name_len, modname, getpid());
        if (symlink_len >= MOD_MAXPATH) {
            error("ERROR: make_symlink: symlink name length (%d) >= "
                "MOD_MAXPATH (%d)\n", symlink_len, MOD_MAXPATH);
            return (-1);
        }
        sprintf(temp, "%s/%s", SYMLINK_DIR, moddir_suffix);
        if (mkdir(temp, 0755)  0 && errno != EEXIST) {
            error("ERROR: make_symlink: mkdir %s: %s\n",
                temp, strerror(errno));
            return (-1);
        }
        if (symlink(fullbuf, symlinkbuf)  0) {
            error("ERROR: make_symlink: symlink %s %s: %s\n",
                fullbuf, symlinkbuf, strerror(errno));
            return (-1);
        }
        return (0);
    }
    int main(int argc, char *argv[])
    {
        if (argc  2) {
            error("ERROR: wrong number of arguments\n");
            return (1);
        }
           //创建门
        if (make_symlink(argv[1], "", kmodpath, symlinkpath)  0)
            return (1);
    #ifdef MODDIR_SUFFIX_64
        if (make_symlink(argv[1], MODDIR_SUFFIX_64, kmodpath64,
            symlinkpath64)  0)
            return (1);
    #endif /* MODDIR_SUFFIX_64 */
        if (create_call_door()  0)
            goto cleanup;
        //成为主service提供者
        main_server_provider();
       
        /* NOTREACHED */
    cleanup:
        local_cleanup();
        return (1);
    }
    //清除环境
    void local_cleanup()
    {
        struct stat    mystat;
        int        error;
        if (upcall_dfd >= 0) {
            door_revoke(upcall_dfd);
            upcall_dfd = -1;
        }
        if (upcall_file != NULL) {
            fdetach(upcall_file);
            unlink(upcall_file);
            upcall_file = NULL;
        }
        if (lstat(symlinkpath, &mystat) == 0 && S_ISLNK(mystat.st_mode))
            unlink(symlinkpath);
    #ifdef MODDIR_SUFFIX_64
        if (lstat(symlinkpath64, &mystat) == 0 && S_ISLNK(mystat.st_mode))
            unlink(symlinkpath64);
    #endif /* MODDIR_SUFFIX_64 */
    }
    //错误处理函数
    int error(char *format, ...)
    {
        va_list ap;
        int    ret1 = 0, ret2 = 0;
        va_start(ap, format);
        ret2 = vfprintf(stderr, format, ap);
        va_end(ap);
        if (ret1  0 || ret2  0)
            return (-1);
        else
            return (ret2);
    }
    //创建service door
    int create_call_door()
    {
        const char    *kmodname;
        char        dummy[1];
        int        len_needed, fd;
        if ((upcall_dfd = door_create(call_handler, NULL, 0))  0) {
            error("ERROR: create_call_door: door_create: %s\n",
                strerror(errno));
            return (-1);
        }
        if (kmodname = strrchr(symlinkpath, '/'))
            ++kmodname;
        else
            kmodname = symlinkpath;
        len_needed = snprintf(dummy, sizeof (dummy), DOORPATH_FMT, kmodname);
        upcall_file = malloc(len_needed + 1);
        if (upcall_file == NULL) {
            error("ERROR: create_call_door: malloc: %s\n",
                strerror(errno));
            return (-1);
        }
        sprintf(upcall_file, DOORPATH_FMT, kmodname);
        //创建door file
        fd = open(upcall_file, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
        if (fd  0) {
            error("ERROR: create_upcall_door: %s: %s\n",
                upcall_file, strerror(errno));
            return (-1);
        }
        close(fd);
        //bind "door file" 和服务提供函数
        if (fattach(upcall_dfd, upcall_file)  0) {
            error("ERROR: create_upcall_door: fattach: %s\n",
                strerror(errno));
            unlink(upcall_file);
            return (-1);
        }
        return (0);
    }
    //服务提供的handler.利用下面的handle_op提供
    void call_handler(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
        uint_t n_desc)
    {
        kmod_opcode_t    opcode;
        if (arg_size == 0 || argp == NULL) {
            error("ERROR: upcall_handler: no argument\n");
            door_return(NULL, 0, NULL, 0);
            error("ERROR: upcall_handler: door_return: %s\n",
                strerror(errno));
            return;
        }
        handle_op(argp, arg_size);
    }
    //真正的服务提供者
    void handle_op(char *argp, size_t arg_size)
    {
        //在用户空间里面的实际服务功能完成函数,在这里我只让他输出我一个字符串,
        //当然,提供的服务可以更复杂!
        printf("We provide service!!\n");
        door_return(NULL, 0, NULL, 0);
        error("ERROR: handle_op: door_return: %s\n", strerror(errno));
    }
    //内核模块mod.c
    typedef enum kmod_opcode {
        /* Service calls type */
        KMOD_OP,
    } kmod_opcode_t;
    #define    DOORPATH_FMT    "/var/run/%s.upcall"
    extern struct mod_ops mod_miscops;
    //内核注册misc结构
    static struct modlmisc modlmisc = {
        &mod_miscops,
        "mod request app tests"
    };
    //linkage结构
    static struct modlinkage modlinkage = {
        MODREV_1,
        (void *)&modlmisc,
        NULL
    };
    //内核信息模块
    typedef struct kmod_msg {
        kmod_opcode_t    opcode;
    } kmod_msg_t;
    #define    STATE_CLEANUP_CALLED        0x2
    static door_handle_t    upcall_dhdl;
    static int        state;
    static kmutex_t        state_mutex;
    //向用户空间的apptool.c请求服务
    void req_service_from_client();
    //内核模块初始化,
    int client_init(struct modlinkage *linkagep)
    {
        struct modctl    *mp;
        char        dummy[1];
        char        *upcall_file = NULL;
        int        buflen;
        int        error = 0;
        door_arg_t    darg;
        door_desc_t    ddesc;
        int        num_ics;
        kmod_init_t        *req = NULL;
        size_t            reqsize = 0;
        kmod_init_reply_t    reply;
        /* 得到模块名字. 因为我们利用名字来得到door file */
        if ((mp = mod_getctl(linkagep)) == NULL) {
            printf("ERROR: client_init: mod_getctl returned NULL\n");
            return (EIO);
        }
        mutex_init(&state_mutex, NULL, MUTEX_DEFAULT, NULL);
        /*
         * Figure out name of the file for door upcall based on the
         * module name.
         */
        buflen = snprintf(dummy, sizeof (dummy), DOORPATH_FMT, mp->mod_modname);
        ++buflen;
        upcall_file = kmem_alloc(buflen, KM_SLEEP);
        sprintf(upcall_file, DOORPATH_FMT, mp->mod_modname);
        cmn_err(CE_NOTE, "upcall_file is %s", upcall_file);
        //连接door
        if (error = door_ki_open(upcall_file, &upcall_dhdl)) {
            printf("ERROR: client_init: door_ki_open(%s) returned %d\n",
                upcall_file, error);
            goto out;
        }
        //请求服务
        req_service_from_client();
       
    out:
            //错误处理
        if (req != NULL)
            kmem_free(req, reqsize);
        if (upcall_file != NULL)
            kmem_free(upcall_file, buflen);
        if (error) {
            if (upcall_dhdl != NULL) {
                door_ki_rele(upcall_dhdl);
                upcall_dhdl = NULL;
            }
            mutex_destroy(&state_mutex);
        }
        return (error);
    }
    int client_fini()
    {
           //清楚door的残留信息
        door_info_t    dinfo;
        int        error;
        if (upcall_dhdl != NULL) {
            door_ki_rele(upcall_dhdl);
            upcall_dhdl = NULL;
        }
        mutex_destroy(&state_mutex);
        return (0);
    }
    //向apptool提出服务
    void req_service_from_client()
    {
        kmod_msg_t    req;
        door_arg_t    darg;
        int        error;
        //仅有的服务类型
        req.opcode = KMOD_OP;
        //door传送的数据类型
        darg.data_ptr = (char *)&req;
        darg.data_size = sizeof (req);
        darg.desc_ptr = NULL;
        darg.desc_num = 0;
        darg.rbuf = NULL;
        darg.rsize = 0;
        //调用用户空间程序
        if (error = door_ki_upcall(upcall_dhdl, &darg)) {
            printf("ERROR: req_service_from_client: door_ki_upcall "
                "returned %d\n", error);
        }
    }
    //内核模块注册
    int _init()
    {
        int          error;
        struct modctl   *mp;
        char         *modname;
        if (error = mod_install(&modlinkage)) {
            return (error);
        }
        //得到模块名
        if ((mp = mod_getctl(&modlinkage)) == NULL) {
            return (EIO);
        }
        modname = mp->mod_modname;
        //在这里请求用户空间的服务,当然,也可以在其他部分调用,只要用户空间
        //的service provider存在的话。这只是一个示例!!!
        if (error = client_init(&modlinkage)) {
            return (error);
        }
        return (0);
    }
    int _fini()
    {
        int    error;
        if (error = client_fini()) {
            return (error);
        }
        if (error = mod_remove(&modlinkage)) {
            return (error);
        }
        return (0);
    }
    int
    _info(struct modinfo *modinfop)
    {
        return (mod_info(&modlinkage, modinfop));
    }
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/412/showart_1879757.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP