Linux内核驱动学习之HelloWorld内核模块!

转载自

(1)https://blog.csdn.net/sh21_/article/details/60878812?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

(2)https://www.cnblogs.com/wangyuezhuiyi/archive/2011/11/15/2250102.html

(3)https://blog.csdn.net/u010632165/article/details/86541941?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight 


内核模块基本原理:

内核模块是可以根据实际需要可以动态加载和卸载到内核中的代码。它们扩展了内核的功能,而无需重启系统,就可以进行模块加载,并工作。

Linux 内核模块(LKM)是可以根据实际需要可以动态加载和卸载到内核中的代码。它们扩展了操作系统内核功能却不需要重新编译内核、启动系统。如果没有内核模块,就不得不反复编译生成操作系统的内核镜像来加入新功能,当附加的功能很多时,还会使内核变得臃肿。一个Linux 内核模块主要由以下几个部分组成:

(1) 模块加载函数(必须):当通过insmod 或modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块相关初始化工作。

(2) 模块卸载函数(必须):当通过rmmod 命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。

(3) 模块许可证声明(必须):模块许可证(LICENCE)声明描述内核模块的许可权限,如果不声明LICENCE,模块被加载时将收到内核被污染的警告。大多数情况下,内核模块应遵循GPL 兼容许可权。Linux2.6 内核模块最常见的是以MODULE_LICENSE(“Dual BSD/GPL”)语句声明模块采用BSD/GPL 双LICENSE。

(4) 模块参数(可选):模块参数是模块被加载的时候可以被传递给他的值,它本身对应模块内部的全局变量。

(5) 模块导出符号(可选):内核模块可以导出符号(symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数。

(6) 模块作者等信息声明(可选)。

一个内核模块至少包含两个函数,模块被加载时执行的初始化函数init_module()和模块被卸载时执行的结束函数cleanup_module()。在最新内核稳定版本2.6 中,两个函数可以起任意的名字,通过宏module_init()和module_exit()注册调用要编译内核模块,把代码嵌进内核空间,首先要获取内核源代码,且版本必需与当前正在运行的版本一致。



helloworld.c

helloworld.c

(1) moudle.h 包含了大量加载模块需要的函数和符号的定义.

(2) init.h 来指定你的初始化和清理函数

(3) MODULE_LICENSE("GPL");指定代码使用哪个许可

           内核认识的特定许可有, 

                "GPL"( 适用 GNU 通用公共许可的任何版本 ), 

                "GPL v2"( 只适用 GPL 版本 2 ), 

                "GPL and additional rights", 

                "Dual BSD/GPL", 

                "Dual MPL/GPL", 

                "Proprietary".

(4) 除此之外还可以包含模块的其他描述性定义

          MODULE_AUTHOR ( 声明谁编写了模块 ), 

          MODULE_DESCRIPION( 一个人可读的关于模块做什么的声明 ), MODULE_VERSION ( 一个代码修订版本号),

          MODULE_ALIAS ( 模块为人所知的另一个名子 ), 

          MODULE_DEVICE_TABLE ( 来告知用户空间, 模块支持那些设备).

(5) static int hello_init(void)

             初始化函数应当声明成静态的,

          static void hello_exit(void) 

               清理函数, 它注销接口, 在模块被去除之前返回所有资源给系统

(6) module_init(hello_init);

           这个宏定义增加了特别的段到模块目标代码中, 表明在哪里找到模块的初始化函数. 没有这个定义, 你的初始化函数不会被调用.

           module_exit(hello_exit);

(7)printk

            在 Linux 内核中定义并且对模块可用; 它与标准 C 库函数 printf 的行为相似. 内核需要它自己的打印函数, 因为它靠自己运行

(8)字串 KERN_ALERT 是消息的优先级,因为使用缺省优先级的消息可能不会在任何有用的地方显示

Makefile

Makefile

(1)obj-m := hello.o

              表明有一个模块要从目标文件 hello.o 建立. 在从目标文件建立后结果模块命名为 hello.ko。

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

              用来定位内核源码目录

(3)PWD := $(shell pwd)

             获得当前目录路径

(4)M=$(PWD) M= 选项

              使 makefile 在试图建立模块目标前, 回到你的模块源码目录

(5)$(MAKE) -C $(KERNELDR) M=$(PWD) modules

              这个命令开始是改变它的目录到用 -C 选项提供的目录下( 就是说, 你的内核源码目录 ). 它在那里会发现内核的顶层 makefile. 这个 M= 选项使 makefile 在试图建立模块目标前, 回到你的模块源码目录. 这个目标, 依次地, 是指在 obj-m 变量中发现的模块列表, 在我们的例子里设成了 module.o.

(6)$(MAKE) -C $(KERNELDR) M=$(PWD) modules_install

             用于模块的安装

(7)rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

             用于make clean清除上次编译生成的文件


模块加载、查看、卸载

insmod   helloworld.ko   #将编译生成的ko文件加载到内核中

lsmod | grep helloworld #查看内核模块是否有helloword

rmmod   helloworld.ko   #删除内核中的ko模块

模块调试:

dmesg | grep "init success" # 在终端查看init函数中,KERN——INFO的打印信息

dmesg | grep "exit success" # 在终端查看exit函数中,KERN——INFO的打印信息

dmesg  #查看内存缓冲区的内容

dmesg | tail -12   #查看内核输出信息,tail -12 显示最后12条;

dmesg | grep word_count |tail -n 2

cat /var/log/syslog | grep word_count |tail -n 2

modinfo  模块名.ko  #查看模块路径、协议、作者等信息


echo  'la zi ji' > /dev/wordcount  #向设备文件写数据

dmesg 


初次编写时出现的问题:

1. 卸载函数exit的返回值必须为void;

2. $(MAKE) -C $(KERNELDIR) M=$(PWD) 后面只能是modules 或空

你可能感兴趣的:(Linux内核驱动学习之HelloWorld内核模块!)