/Files/csjiaxin/force_rmmod.rar
在内核模块的插入过程中,如果初始化函数发生错误,比如内核崩溃,则使用rmmod 会提示
ERROR: Module export is in use
解决方法如下:
struct module{
enum module_state state;
/* Reference counts */
struct module_ref ref[NR_CPUS];
}
enum module_state
{
MODULE_STATE_LIVE,
MODULE_STATE_COMING,
MODULE_STATE_GOING,
};
在执行初始化函数前,内核会将为此插入模块新分配的module结构的引用计数初始化为1,状态state设置为MODULE_STATE_COMING,由于初始化失败,所以module结构的引用计数和状态就停留于此。
为在rmmod->delete_module->sys_delete_module中有以下的判断语句:
/* Doing init or already dying? */
if (mod->state != MODULE_STATE_LIVE) {
/* FIXME: if (force), slam module count and wake up
waiter --RR */
DEBUGP("%s already dying\n", mod->name);
ret = -EBUSY;
goto out;
}
if (!forced && module_refcount(mod) != 0)
wait_for_zero_refcount(mod);
则可以看出只能卸载状态state为MODULE_STATE_LIVE,ref数组中所有CPU引用计数之和为0的模块,这就导致rmmod无法卸载由于初始化函数崩溃的模块。
明白了原理,具体的实现就比较容易了:
1 root@lstar-desktop:~/force_rmmod# cat /proc/kallsyms | grep modules
c069b9a0 d modules
2 编写另外一个内核模块,去操作内核所有module组成的链表(表头变量即为modules,我们在第一步已看到他的地址) 其中module_name,是传递过来的需要卸载的内核模块的名称
struct list_head *modules=(struct list_head *)0xc069b9a0;
struct module *mod=0;
struct module *list_mod;
int i;
int zero=0;
list_for_each_entry(list_mod,modules,list){
if(strcmp(list_mod->name,module_name) == 0)
mod=list_mod;
}
mod->state=MODULE_STATE_LIVE;
for (i = 0; i < NR_CPUS; i++){
mod->ref[i].count=*(local_t *)&zero;
}
将这个内核模块使用insmod插入,执行到初始化函数的时候,搜索modules链表,找到所要操作的module对象,修改其引用计数及状态,结束之后就可以使用rmmod命令,卸载之前的模块了。
***********************
今天在写内核模块时,又发现了一个由于卸载过程module_exit指定的函数失败导致内核模块不能卸载,则需要多修改几个条件,需要将module的init和exit修改为NULL,则可以完成卸载。
1 mod->init=0;
2 mod->exit=0;
实验环境 ubuntu9.04 kernel2.6.28-19,源码见附件
可以完善的地方,
1 内核模块直接从/proc/kallsyms中读取modules地址
2 在将状态设置为MODULE_STATE_LIVE和清空引用计数后,可以直接通过
/* Free a module, remove from lists, etc (must hold module_mutex). */
static void free_module(struct module *mod)
函数删除内核模块,则无需调用 rmmod删除。