关于模块insmod和rmmod出错的解决方案

来源:互联网 发布:铁路数据服务平台 编辑:程序博客网 时间:2024/06/04 19:11

问题描述

这两天在学习Linux内核的时候遇到了一个让人抓狂的问题,问题如下: 在编写内核模块时,代码中出现错误,但是编译器并没有检查出来,比如一些内存的问题,访问了空指针等,我遇到的是不小心对一个NULL指针进行了赋值比较,导致在insmod **.ko的时候出现了错误,第一次直接提示KILL(或已杀死)。当不明所以地第二次insmod的时候会阻塞,应该说卡死在那里吧(即使已经把代码修改成功)。


问题分析

因为代码的原因导致这个问题的发生是很可悲的啊,通过lsmod查看已加载的模块,会发现之前insmod的模块已经存在,但是无法卸载,而且引用计数不为0。再通过cat /proc/modules 看到此模块的状态为loading,而不是living。所以要卸载此模块,就需要将这两个东西修改掉,将状态和引用计数都修改为0。


问题解决

找到问题,分析完问题,就可以开始解决问题了。 由于通过手动insmod加载的module是临时的,所以通过重启机器是能够结果上述问题的,但是我们不能每次都重启吧,所以我们再编写一个模块,通过insmod这个模块来修改那个错误模块的相关属性。 通过查看内核源码,看到struct module结构包含的以下几个成员:

struct module {    enum module_state state; //模块的状态,这是一个枚举变量    char name[MODULE_NAME_LEN];   //模块的名称    struct module_ref __percpu *refptr;   //模块的引用计数}
enum module_state {    MODULE_STATE_LIVE,  /* Normal state. */    MODULE_STATE_COMING,    /* Full formed, running module_init. */    MODULE_STATE_GOING, /* Going away. */    MODULE_STATE_UNFORMED,  /* Still setting it up. */};

我们修改状态和引用计数即可。

  • 修改引用计数通过以下两个函数来实现
    int try_module_get(struct module *); //增加module的引用计数
    void module_put(struct module *); //减少module的引用计数
  • 修改状态直接可以对module的成员state进行赋值

    以下是我写的一些测试代码,好了废话不多说,贴代码

/*print_module.c*/#include<linux/kernel.h>#include<linux/init.h>#include<linux/list.h>#include<linux/module.h>MODULE_LICENSE("GPL");/***每次重启内核时都要通过这个命令来获取**sudo cat /pro/kallsyms | grep [^_]modules$ */#define MODULES 0xffffffff8ec5b470 /*在两个修改方案上选择*/#define DEBUG_MODULE 0 /*是否启动调试*/#define DEBUG 1  static char *name = "testtesttest";/*模块参数,传入一个模块的名称*/module_param(name,charp,S_IRUGO); static int __init print_module_init(void) {    struct list_head *module_head;    struct list_head *pos;    struct module *p;    int i= 0;    int ret;    printk(KERN_INFO "print module init\n");    module_head = (struct list_head*)MODULES;    /*遍历所有的module*/    list_for_each(pos,module_head) {        p = list_entry(pos,struct module,list);#if DEBUG#if DEBUG_MODULE/*如果匹配到这个模块,修改状态为living,增加引用计数*/        if(strcmp(p->name,name)==0){            p->state = MODULE_STATE_LIVE;            if((ret = try_module_get(p)) == 0) {                printk(KERN_ALERT "try_module_get error\n");            }            printk(KERN_INFO "%s ret is inc\n",p->name);        }#else/*降低匹配到的模块的引用计数*/        if(strcmp(p->name,name) == 0) {            module_put(p);            printk(KERN_INFO "%s ref is dec\n",p->name);        }#endif#endif    }    return 0;}static void __exit print_module_exit(void){    printk(KERN_INFO "print module exit\n");}module_init(print_module_init);module_exit(print_module_exit);

Makefile如下

obj-m:= print_module2.oKDIR:= /lib/modules/$(shell uname -r)/buildall:    $(MAKE) -C $(KDIR) M=$(shell pwd) modulesclean:    $(MAKE) -C $(KDIR) M=$(shell pwd) clean

编译通过后,insmod print_module.ko name=xxxx进行加载,xxx是出错的那个模块
在过程中可以通过lsmod和cat /proc/modules 查看模块相关信息

1 1
原创粉丝点击