Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3514792
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类:

2015-03-31 23:30:51

1. 简介

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


但是,能否反其道而行之呢?也就是说,能不能从内核里面的模块直接调用应用层的函数,并且得到执行呢?我们都知道,在应用层编程有很多好处,比如,可以利用现成的代码,有好的调试工具,等等很多。如果可以把一部分程序从内核空间移到用户空间,应该对开发有很大方便。

2. 原理

我编写了一个实验程序,可以从内核空间调用应用层的程序,该程序在最新的Solaris系统实验过。具体方法是利用Solaris提供的doorRPC调用)来完成。我们都知道doorSolaris首创并且进入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. 扩展

    1. 利用door,很容易在用户空间内写一个daemon进程,用于向内核模块提供服务,在用户空间完成一些操作。这个daemon中最直接的可以应用在log功能上,也就是说,我们可以利用这个daemon给内核模块提供接口,将内核空间的一些信息直接dump出来,写到我们自己的log里面去。当然,还有其他功能可以扩展,就看设计者想用它干什么了!

    2. 同样,我们可以把这个体系反过来理解。在内核里面提供一个服务,在用户空间的程序通过door_call来直接调用内核的程序,在内核里面完成相应的功能。作者现在没有编写相应的程序来印证这一点,但是,这应该很容易。就是把上面的程序结构反过来应用就可以了。


4. 具体程序

下面的程序在最新的Solaris下面运行过。因为不清楚Linux内核是否提供了相应的功能,没有在Linux平台上实验。具体程序请见下面。值得注意的是,这个程序只是一个示例程序,它只是证明了从内核空间是可以调用用户空间函数的,并没有特别的意义。但是它是一个可以扩展的架构,我们可以在这个体系下充分的完成其他功能。

运行示例:

编译下面的两个程序,在应用层空间的生成apptool的文件,同时生成Solaris的kernel module--testmod.

# ./apptool testmod <--启动door服务,此时服务在应用层监听

# modload ./testmod <--注册testmod模块,在_init里面向上面的程序请求服务


这时,可以看到,在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));
}

阅读(1654) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~