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

问题描述

这两天在学习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
#include
#include
#include

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.o

KDIR:= /lib/modules/$(shell uname -r)/build

all:
    $(MAKE) -C $(KDIR) M=$(shell pwd) modules
clean:
    $(MAKE) -C $(KDIR) M=$(shell pwd) clean

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

你可能感兴趣的:(linux内核模块)