- 论坛徽章:
- 0
|
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 |
|