大家都知道内核编译完成后,启动时加载到系统中,系统运行的时候不能再改变内核的功能,如果想修改,必须要重新编译,然后替代旧的内核。内核模块kernel module则提供了在系统在运行时,动态的修改内核功能的一个机制,而不必重新编译内核。
我在工作中倒是没有用非用到kernel module的时候,但是有时候为查看内核数据,动态修改某些行为,通过kernel module还是很方便的。
在/usr/src/share/examples/kld/里有一些kernel module的例子,我们可以举一个例子来说明kernel module的使用。参考syscall这个目录,查看一下syscall.c文件。
- /*
-
* The function for implementing the syscall.
-
*/
-
-
static int
-
hello (struct thread *td, void *arg)
-
{
-
printf ("hello kernel\n");
-
return 0;
-
}
-
-
/*
-
* The `sysent' for the new syscall
-
*/
-
-
static struct sysent hello_sysent = {
-
0, /* sy_narg */
-
hello /* sy_call */
-
};
-
-
/*
-
* The offset in sysent where the syscall is allocated.
-
*/
-
-
static int offset = NO_SYSCALL;
-
-
/*
-
* The function called at load/unload.
-
*/
-
-
static int
-
load (struct module *module, int cmd, void *arg)
-
{
-
int error = 0;
-
-
switch (cmd) {
-
case MOD_LOAD :
-
printf ("syscall loaded at %d\n", offset);
-
break;
-
case MOD_UNLOAD :
-
printf ("syscall unloaded from %d\n", offset);
-
break;
-
default :
-
error = EOPNOTSUPP;
-
break;
-
}
-
return error;
-
}
-
-
SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
收看要看的是SYSCALL_MODULE宏,它是对DECLARE_MODULE的封装,本module是syscall module。
- #define SYSCALL_MODULE(name, offset, new_sysent, evh, arg) \
-
static struct syscall_module_data name##_syscall_mod = { \
-
evh, arg, offset, new_sysent, { 0, NULL, AUE_NULL } \
-
}; \
-
\
-
static moduledata_t name##_mod = { \
-
#name, \
-
syscall_module_handler, \
-
&name##_syscall_mod \
-
}; \
-
DECLARE_MODULE(name, name##_mod, SI_SUB_SYSCALLS, SI_ORDER_MIDDLE)
可以理解为声明一个module,DECLARE_MODULE定义如下
- #define DECLARE_MODULE(name, data, sub, order) \
-
MODULE_METADATA(_md_##name, MDT_MODULE, &data, #name); \
-
SYSINIT(name##module, sub, order, module_register_init, &data) \
-
struct __hack
具体是什么意思,不太清楚,可以理解像内核注册了个module。
接下来看SYSCALL_MODULE的参数:
- offset:是一个输入输出参数,是syscall的number,如果设置为NO_SYSCALL,kernel会为我们分配一个nubmer,我们可以自己指定一个数字,210-219之间,其他的会注册失败
-
new_sysent:syscall回调函数的相关信息,比如参数
-
evh:注册本module时候回调函数,需要处理MOD_LOAD,MOD_UNLOAD等信息
-
arg:调用注册module回调函数需要传入的参数,我觉得在一个内核模块注册多个syscall的时候有用(用于区分哪个syscall)
hello_sysent定义了syscall的执行函数的相关信息,比如参数是0个,函数名字是hello。
那我们如果想定义有参数的syscall怎么办呢?
需要做三步:
1.修改hello_sysent:
- static struct sysent hello_sysent = {
-
1, /* sy_narg */
-
hello /* sy_call */
-
}
2.定义一个参数结构体:
- struct hello_args
-
{
-
int arg1;
-
};
3.修改syscall 函数hello
- static int
-
hello (struct thread *td, struct hello_args *arg)
-
{
-
printf ("hello, arg is:%d\n", arg->arg1);
-
return 0;
-
}
这样调用回调函数时候,就可以打印传入的参数了。怎么调用syscall呢?
- int
-
main(int argc, char **argv)
-
{
-
char *endptr;
-
int syscall_num;
-
struct module_stat stat;
-
-
stat.version = sizeof(stat);
-
modstat(modfind("syscall"), &stat);
-
syscall_num = stat.data.intval;
-
return syscall (syscall_num, 4);
-
}
上面就是根据module名字找syscall id,然手调用syscall,如果知道id,也可以直接调用,那个4是hello的参数。
还有一个问题,可不可以在一个ko文件里定义多个syscall呢?答案可以,多次调用SYSCALL_MODULE就可以了,但每次的module名字不可以相同,这里贴一下完整的代码
-
#include <sys/types.h>
-
#include <sys/param.h>
-
#include <sys/proc.h>
-
#include <sys/module.h>
-
#include <sys/sysproto.h>
-
#include <sys/sysent.h>
-
#include <sys/kernel.h>
-
#include <sys/systm.h>
-
-
void* p = 0;
-
-
struct myalloc_args
-
{
-
int count;
-
};
-
-
/*
-
* The function for implementing the syscall.
-
*/
-
-
static int
-
my_alloc (struct thread *td, struct myalloc_args *arg)
-
{
-
printf ("alloc, arg is:%d\n", arg->count);
-
return 0;
-
}
-
-
static int
-
my_release (struct thread *td, struct myalloc_args *arg)
-
{
-
printf ("release, arg is:%d\n", arg->count);
-
return 0;
-
}
-
-
/*
-
* The `sysent' for the new syscall
-
*/
-
-
static struct sysent my_sysent[2] = {
-
{
-
1, /* sy_narg */
- (void*)my_alloc /* sy_call */
-
},
-
{
-
1, /* sy_narg */
- (void*)my_release /* sy_call */
-
}
-
};
-
-
/*
-
* The offset in sysent where the syscall is allocated.
-
*/
-
#if 0
-
static int offset[] = {NO_SYSCALL,NO_SYSCALL};
-
#endif
-
static int offset[] = {212,213};
-
-
char alloc_mod[] = "alloc";
-
char release_mod[] = "release";
-
-
/*
-
* The function called at load/unload.
-
*/
-
-
static int
-
load (struct module *module, int cmd, void *arg)
-
{
-
int error = 0;
-
int modn = 0;
-
-
if(arg == alloc_mod){
-
modn = 0;
-
}
-
if(arg == release_mod){
-
modn = 1;
-
}
-
-
switch (cmd) {
-
case MOD_LOAD :
-
printf ("syscall %s loaded at %d\n",(char*)arg, offset[modn]);
-
break;
-
case MOD_UNLOAD :
-
printf ("syscall unloaded from %d\n", offset[modn]);
-
break;
-
default :
-
error = EOPNOTSUPP;
-
break;
-
}
-
return error;
-
}
-
-
SYSCALL_MODULE(mymodule, &offset[0], &my_sysent[0], load, (void*)alloc_mod);
-
SYSCALL_MODULE(mymodule1, &offset[1], &my_sysent[1], load, (void*)release_mod);
阅读(1442) | 评论(0) | 转发(0) |