强力卸载内核模块

概述

 

内核为2.6.32。

卸载模块target时,不管是否为强制卸载,都输出:

ERROR:Module target is in use.

用lsmod查看target,发现Used by计数为1,而据我所知,没有其它模块依赖target。

编写模块检查target的module结构,发现:

target->state == 0 // 模块存活

module_refcount(target) == 1 // 模块引用计数为1

list_empty(target->modules_which_use_me) == 1 // 模块依赖列表为空

这个就很奇怪,模块的引用计数为1,却没有引用者。

这可能是模块插入内核时出错而引起的,这里先不研究,先关注怎么把它强制卸载掉,虽然

insmod加载是临时的,所以通过重启电脑可以解决一些问题,但是不能总是依靠重启啊。

解决方法:编写模块mymod中把问题模块target的引用计数置为0,就可以顺利卸载掉target了!

 

代码

 

#include
#include
#include
#include
#include

static int __init mymod_init(void)
{
        struct module *mod,*relate;
        int cpu;

        // 打印本模块的模块名和模块状态
        printk(KERN_ALERT"[insmod mymod] name:%s state:%d\n",THIS_MODULE->name,THIS_MODULE->state);

       // 遍历模块列表,查找target模块
        list_for_each_entry(mod,THIS_MODULE->list.prev,list)
        {
                if(strcmp(mod->name,"target")==0) { 

                        // 打印target的模块名、模块状态、引用计数
                        printk(KERN_ALERT"name:%s state:%d refcnt:%u ",mod->name,mod->state,module_refcount(mod));

                        // 打印出所有依赖target的模块名
                        if(!list_empty(&mod->modules_which_use_me)) { 
                                list_for_each_entry(relate,&mod->modules_which_use_me,modules_which_use_me)
                                        printk(KERN_ALERT"%s ",relate->name);
                        } else
                                printk(KERN_ALERT"used by NULL\n");

                        // 把target的引用计数置为0
                        for_each_possible_cpu(cpu)
                                local_set(__module_ref_addr(mod,cpu),0);

                        // 再看看target的名称、状态、引用计数
                        printk(KERN_ALERT"name:%s state:%d refcnt:%u\n",mod->name,mod->state,module_refcount(mod));
                }
        }
        return 0;
}

static void __exit mymod_exit(void)
{
        printk(KERN_ALERT"[rmmod mymod] name:%s state:%d\n",THIS_MODULE->name,THIS_MODULE->state);
}

module_init(mymod_init);
module_exit(mymod_exit);

MODULE_AUTHOR("Zhangsk");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Why module can not be removed");

 

内核

 

@include/linux/module.h:

extern struct module __this_module;
#define THIS_MODULE (&__this_module);

enum module_state{ 
    MODULE_STATE_LIVE; // 模块存活,0 
    MODULE_STATE_COMING; // 正在加载模块,1 
    MODULE_STATE_GOING; // 正在卸载模块,2
}; 

struct module { 
    enum module_state state; // 模块状态 

    /* Member of list of modules */ 
    struct list_head list; // 内核模块链表 

    /* Unique handle for this module */ 
    char name[MODULE_NAME_LEN]; //模块名称 

    ...

#ifdef CONFIG_MODULE_UNLOAD
     /* What modules depend on me? */ 
    struct list_head modules_which_use_me;

     /* Who is waiting for us to be unloaded */ 
    struct task_struct *waiter;

     /* Destruction function. */ 
    void (*exit) (void);

#ifdef CONFIG_SMP 
    char *refptr;
#else 
    local_t ref;
#endif
#endif 

    ...

};

static inline local_t *__module_ref_addr(struct module *mod, int cpu)
{#ifdef CONFIG_SMP
     return (local_t *) (mod->refptr + per_cpu_offset(cpu));
#else
     return &mod->ref;
#endif
}

@include/asm-generic/atomic.h:

typedef atomic64_t atomic_long_t;

@include/linux/types.h:

typedef struct { 
    volatile int counter;
} atomic_t;

#ifdef CONFIG_64BIT
typedef struct { 
    volatile long counter;
} atomic64_t;
#endif

@arch/x86/include/asm/local.h:

typedef struct {
    atomic_long_t a;
} local_t; 

#define local_read(l) atomic_long_read(&(l)->a)
#define local_set(l, i) atomic_long_set(&(l)->a, (i))

//此外还有加减操作

@include/asm-generic/percpu.h:

#ifdef CONFIG_SMP

/** per_cpu_offset() is the offset that has to be added to a percpu variable to get the instance for
  * a certain processor. * Most arches use the __per_cpu_offset array for those offsets but some arches have their own
  * ways of determining the offset (x86_64, s390). 
  */

#ifndef __per_cpu_offset
extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])
#endif

 

你可能感兴趣的:(Kernel)