linux 内核层到应用层通信,如何在kernel里调用应用层函数

1.简介

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

c5b0ec95c8c173c827d7f85ae763af9a.gif

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

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));

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值