0 问题:某项目中,关于一种调用设备驱动程序,出现异常时,驱动设备无法正常退出(lsmod 显示驱动设备被占用、无法rmmod 退出),也无法继续使用的问题。

1 linux 模块

内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),我们简称为模块。模块具有以下特点:

模块本身不被编译入内核映像,从而控制了内核的大小 。

模块一旦被加载,它就和内核中的其他部分一样 。

模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。

2 编译内核模块

hello 模块

#include <linux/module.h> //所有模块都需要的头文件
#include <linux/init.h>   // init&exit相关宏
#include <linux/kernel.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("hello world module");static int __init hello_init(void)
{printk(KERN_WARNING "hello world.\n");return 0;
}
static void __exit hello_exit(void)
{printk(KERN_WARNING "hello exit!\n");
}module_init(hello_init);
module_exit(hello_exit);

Makefile

ifneq ($(KERNELRELEASE),)
obj-m :=hello.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
all:make -C $(KDIR) M=$(PWD) modules
clean:rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
endif

使用

make

sudo insmod hello.ko

sudo rmmod hello

dmesg

[10611.395806] hello world.
[10644.832158] hello exit!

KERNELRELEASE

这个 makefile 在一次典型的建立中要被读 2 次。

KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次(刚开始)读取执行此Makefile时,KERNELRELEASE没有被定义(为空),所以make将读取执行else之后的内容。如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C(KDIR)指明跳转到内核源码目录下读取那里的Makefile,M=(PWD)表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容(指 obj-m :=hello.o)。else之前的内容为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名。

以上介绍了内核模块的概念以及内核模块的编译。

3 强制卸载内核模块

发现一篇非常好的文章,对照无法强制卸载内核模块的各个情况,相应进行了介绍。

原文链接:Linux强制卸载内核模块(由于驱动异常导致rmmod不能卸载)

对照文章,本人遇到的驱动被占用问题属于以下这种类型。

引用计数在有其它模块或者内核本身引用的时候不为0,要卸载就要等待它们不引用为止 used 需要通过外部修正的方式, 将驱动的引用计数置 0 即可

(1)现象

rmmod: ERROR: Module XXX is in use

(2)其本质就是模块的模块的引用计数不为 0, 要解决此类问题, 只需要将模块的引用计数强制置为 0 即可.

  1. 查找到 none_exit 模块的内核模块结构 struct moudle, 可以通过 find_module 函数查找到, 也可以参照 find_module 函数实现.

  2. 重置模块的引用计数

    //  清除驱动的引用计数
    for_each_possible_cpu(cpu)
    {local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);//local_set(__module_ref_addr(mod, cpu), 0);//per_cpu_ptr(mod->refptr, cpu)->decs;//module_put(mod);
    }
    atomic_set(&mod->refcnt, 1);

4 强制卸载驱动源码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/list.h>
#include <asm-generic/local.h>
#include <linux/platform_device.h>
#include <linux/kallsyms.h>
#include <linux/sched.h>/**  加载模块的时候, 传递字符串到模块的一个全局字符数组里面**  module_param_string(name, string, len, perm)**  @name   在加载模块时,参数的名字*  @string 模块内部的字符数组的名字*  @len    模块内部的字符数组的大小*  #perm   访问权限** */
static char *modname = NULL;
module_param(modname, charp, 0644);
MODULE_PARM_DESC(modname, "The name of module you want do clean or delete...\n");//#define CONFIG_REPLACE_EXIT_FUNCTION#ifdef CONFIG_REPLACE_EXIT_FUNCTION
//  此处为外部注册的待卸载模块的exit函数
//  用于替代模块原来的exit函数
//  注意--此函数由于需要被待删除模块引用, 因此不能声明为static
/* static */ void force_replace_exit_module_function(void)
{///  此处完善待卸载驱动的 exit/cleanup 函数/printk("module %s exit SUCCESS...\n", modname);
//    platform_device_unregister((struct platform_device*)(*(int*)symbol_addr));
}
#endif  //  CONFIG_REPLACE_EXIT_FUNCTIONstatic int force_cleanup_module(char *del_mod_name)
{struct module   *mod = NULL, *relate = NULL;int              cpu;
#ifdef CONFIG_REPLACE_EXIT_FUNCTIONvoid            *origin_exit_addr = NULL;
#endif///  找到待删除模块的内核module信息/
#if 0//  方法一, 遍历内核模块树list_mod查询struct module *list_mod = NULL;/*  遍历模块列表, 查找 del_mod_name 模块  */list_for_each_entry(list_mod, THIS_MODULE->list.prev, list){if (strcmp(list_mod->name, del_mod_name) == 0){mod = list_mod;}}/*  如果未找到 del_mod_name 则直接退出  */if(mod == NULL){printk("[%s] module %s not found\n", THIS_MODULE->name, modname);return -1;}
#endif//  方法二, 通过find_mod函数查找if((mod = find_module(del_mod_name)) == NULL){printk("[%s] module %s not found\n", THIS_MODULE->name, del_mod_name);return -1;}else{printk("[before] name:%s, state:%d, refcnt:%u\n",mod->name ,mod->state, module_refcount(mod));}///  如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出//*  如果有其他模块依赖于 del_mod  */if (!list_empty(&mod->source_list)){/*  打印出所有依赖target的模块名  */list_for_each_entry(relate, &mod->source_list, source_list){printk("[relate]:%s\n", relate->name);}}else{printk("No modules depond on %s...\n", del_mod_name);}///  清除驱动的状态和引用计数///  修正驱动的状态为LIVEmod->state = MODULE_STATE_LIVE;//  清除驱动的引用计数for_each_possible_cpu(cpu){local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);//local_set(__module_ref_addr(mod, cpu), 0);//per_cpu_ptr(mod->refptr, cpu)->decs;//module_put(mod);}atomic_set(&mod->refcnt, 1);#ifdef CONFIG_REPLACE_EXIT_FUNCTION///  重新注册驱动的exit函数/origin_exit_addr = mod->exit;if (origin_exit_addr == NULL){printk("module %s don't have exit function...\n", mod->name);}else{printk("module %s exit function address %p\n", mod->name, origin_exit_addr);}mod->exit = force_replace_exit_module_function;printk("replace module %s exit function address (%p -=> %p)\n", mod->name, origin_exit_addr, mod->exit);
#endifprintk("[after] name:%s, state:%d, refcnt:%u\n",mod->name, mod->state, module_refcount(mod));return 0;
}static int __init force_rmmod_init(void)
{return force_cleanup_module(modname);
}static void __exit force_rmmod_exit(void)
{printk("=======name : %s, state : %d EXIT=======\n", THIS_MODULE->name, THIS_MODULE->state);
}module_init(force_rmmod_init);
module_exit(force_rmmod_exit);MODULE_LICENSE("GPL");

5 Makefile 

MODULE_NAME := force_rmmod
#MODCFLAGS:=-O2 -Wall -DMODULE -D__KERNEL__ -DLINUX -std=c99
#EXTRA_CFLAGS  += $(MODULE_FLAGS) $(CFG_INC) $(CFG_INC)
EXTRA_CFLAGS  += -g -std=gnu99  -Wfatal-errors ifneq ($(KERNELRELEASE),)  # kernelspaceobj-m := $(MODULE_NAME).oelse                        # userspaceCURRENT_PATH ?= $(shell pwd)
LINUX_KERNEL ?= $(shell uname -r)
LINUX_KERNEL_PATH ?= /lib/modules/$(LINUX_KERNEL)/buildCURRENT_PATH := $(shell pwd)modules:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modulesmodules_install:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules_installclean:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) cleanrm -f modules.order Module.symvers Module.markers.PHNOY:modules modules_install cleanendif

6 使用方法

# 通过 `modname` 制定待卸载驱动的信息

sudo insmod force_rmmod.ko modname=卸载驱动名称

sudo rmmod 卸载驱动名称

linux 内核模块 强制卸载相关推荐

  1. 一文了解linux 内核模块 强制卸载

    [推荐阅读] 需要多久才能看完linux内核源码? 概述Linux内核驱动之GPIO子系统API接口 一篇长文叙述Linux内核虚拟地址空间的基本概括 0 问题:某项目中,关于一种调用设备驱动程序,出 ...

  2. 在 Linux 中强制卸载的 3 种方法显示“设备正忙”

    在某些情况下,当您尝试卸载文件系统尤其是 NFS 时,它会显示"设备正忙"消息.当 NFS 服务器有一些问题(主要是无法访问)并且您有一个软 NFS 挂载时,通常会发生这种情况. ...

  3. Linux下强制卸载已安装的php!

    原先在centOS6.6上已经安装好了Lamp环境,因为php5.3版本比较低,就使用了remi源升级到5.5,然后问题就来了:项目中使用的php-xml在centOS下找不到yum安装包,而且其他的 ...

  4. linux怎么强制解挂,linux下强制卸载挂接点——umount+Fuser命令详解

    fuser -km /soft umount /soft Linux命令Fuser详解 使用文件或者套节字来表示识别进程.我常用的他的两个功能:查看我需要的进程和我要杀死我查到的进程 比如当你想umo ...

  5. linux下强制卸载命令,强制删除rpm包的方法

    删除软件 要删除软件非常简单,只要执行下面的命令就行: # rpm –e xanim 这时,用户要注意使用的是软件的名称xanim,而不是软件包的名称xanim-27.64-3.i386.rpm. 如 ...

  6. c需要实现安装卸载Linux模块,Linux内核模块编译与加载

    Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用所需要的组件? 方法一: 把所有的组件都编译进内核文件,即Zlmage或bzlmage,但会导致两个问题:一是生成的内核文件过大:二是如 ...

  7. 强制卸载linux 磁盘

    转载来自http://www.xitongzhijia.net/xtjc/20150610/50557.html 对于Linux系统下挂载的文件系统,通常情况下是无法卸载的,可通过命令进行强行卸载,下 ...

  8. Linux 强制卸载挂载点---fuser 命令详解

    Linux fuser 命令详解 强制卸载挂载点 如果你的光盘挂载在/mnt/cdrom 使用umount /dev/cdrom提示busy 可以使用以下命令强行卸载. # fuser -km /mn ...

  9. linux强制卸载_如何强制Linux卸载文件系统?

    linux强制卸载 Some time, Linux fails to unmount a filesystem reporting "device is busy". I und ...

最新文章

  1. 计算机编码技术ppt,计算机编码技术.ppt
  2. MongoDB中_id(ObjectId)生成
  3. 王道计算机考研 数据结构 (树与二叉树)
  4. ITK:处理矢量图像的N个分量
  5. 同源策略_如何支持跨域
  6. 华为P50系列已适配鸿蒙OS 2.0 Beta2:有望4月亮相
  7. FD.io VPP:探究分段场景下vlib_buf在收发包的处理(dpdk_plugin.so)、rte_mbuf与vlib_buf 关系
  8. 扩展类载入器的载入问题
  9. Android开发笔记(一百四十)Word文件的读取与显示
  10. 《精通Spring4.X企业应用开发实战》读后感第六章(国际化)
  11. centos7安装uwsgi报错_nginx + uwsgi 发布django项目!(linux为centos7)
  12. 漏洞挖掘分析技术总结
  13. Azure DevOps Server(TFS) 客户端分析
  14. 1、Linux下搭建研究live555的开发环境
  15. 我屮艸芔茻!!!什么情况,就刚才,百年之功废于一旦!!博客园,你TM想吃翔了吧!...
  16. 微信小程序-获取用户头像信息以及修改用户头像
  17. 基于TensorFlow的Cats vs. Dogs(猫狗大战)实现和详解(1)
  18. Fiddler抓包解析https且配置安卓手机CA证书、抓取微信数据包
  19. windows 10 Quick Assist 远程协助工具
  20. 这本《零信任网络》上架两个月,一直是程序员们最青睐的网络安全书

热门文章

  1. Rust社区运营3周年总结
  2. 使用Siri玩转苹果设备
  3. macOS装机必备应用下载合集(2018年8月更新)
  4. 非锐化掩蔽和高提升滤波
  5. 【Effective Objective-C】——熟悉Objective-C
  6. 肇庆市地方税务局采购容灾备份系统
  7. 搭建个人网站,用虚拟主机还是云服务器好呢?
  8. Double Dispatch and visitor patten-双重分发与访问者模式
  9. Excel日期怎么相加 edate函数
  10. 利用阿里云PAI 实现销量预测