Linux设备驱动程序学习(0)-Hello, world!模块
2010-11-15 14:27
537 查看
一个学习Linux设备驱动程序都会碰到的第一个例程:
我将其复制到我的工作目录,并编写了一个简单的Makefile文件:
说实话,以上是我参考了《Linux设备驱动程序(第3版)》的Makefile源码修改得来的。我对Makefile不是很了解,是该好好学习学习了!然后就是make modules 、 make modules_install 。
在我的开发板上的操作:
学习心得:(1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。(2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。(3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。(4)要十分注意驱动程序的并发处理。(5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。(6)内核代码不能实现浮点书运算。(7)Makefile文件分析:
符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。(10)所有模块代码中都包含一下两个头文件:
(11)所有模块代码都应该指定所使用的许可证:
此外还有可选的其他描述性定义:
上述
清除函数的实际定义通常如下:
(13) Linux内核模块的初始化出错处理一般使用“goto”语句。通常情况下很少使用“goto”,但在出错处理是(可能是唯一的情况),它却非常有用。在大二学习C语言时,老师就建议不要使用“goto”,并说很少会用到。在这里也是我碰到的第一个建议使用“goto”的地方。“在追求效率的代码中使用goto语句仍是最好的错误恢复机制。”--《Linux设备驱动程序(第3版)》以下是初始化出错处理的推荐代码示例:
(14)模块参数:内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变。以下是我的实验程序:
实验结果是 :
我这个实验除了对参数的改变进行实验外,我的一个重要的目的是测试“
(15)“#include <
# include < linux/ init. h> # include < linux/ module. h> MODULE_LICENSE( "Dual BSD/GPL" ) ; static int hello_init( void ) { printk( KERN_ALERT "Hello, Tekkaman Ninja !/n" ) ; return 0; } static void hello_exit( void ) { printk( KERN_ALERT "Goodbye, Tekkaman Ninja !/n Love Linux !Love ARM ! Love KeKe ! /n " );}module_init(hello_init);module_exit(hello_exit); |
KERNELDIR = /home/tekkaman/working/SBC2440/linux-2.6.22.2 # The current directory is passed to sub-makes as argumentPWD := $(shell pwd)INSTALLDIR = /home/tekkaman/working/rootfs/lib/modulesCROSS_COMPILE =/home/tekkaman/working/crosstool-gcc410-k26222/gcc-4.1.0-glibc-2.3.2/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-CC = $(CROSS_COMPILE)gccobj-m := hello.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install: cp hello.ko $(INSTALLDIR)clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions.PHONY: modules modules_install clean |
[root@Tekkaman-Ninja Helloworld]# make modulesmake -C /home/tekkaman/working/SBC2440/linux-2.6.22.2 M=/home/tekkaman/working/Linuxdriver/Helloworld modulesmake[1]: Entering directory `/home/tekkaman/working/SBC2440/linux-2.6.22.2' CC [M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/tekkaman/working/Linuxdriver/Helloworld/hello.mod.o LD [M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.komake[1]: Leaving directory `/home/tekkaman/working/SBC2440/linux-2.6.22.2'[root@Tekkaman-Ninja Helloworld]# make modules_installcp hello.ko /home/tekkaman/working/rootfs/lib/modules[root@Tekkaman-Ninja Helloworld]# |
[Tekkaman2440@SBC2440V4]#cd /lib/modules/[Tekkaman2440@SBC2440V4]#lscs89x0.ko hello.ko p80211.ko prism2_usb.ko[Tekkaman2440@SBC2440V4]#insmod hello.koHello, Tekkaman Ninja ![Tekkaman2440@SBC2440V4]#lsmodModule Size Used by Not taintedhello 1376 0[Tekkaman2440@SBC2440V4]#rmmod helloGoodbye, Tekkaman Ninja !Love Linux !Love ARM ! Love KeKe ![Tekkaman2440@SBC2440V4]#lsmodModule Size Used by Not tainted[Tekkaman2440@SBC2440V4]# |
obj-m := hello.o 代表了我们要构造的模块名为hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果
hello.o是由其他的源文件生成(比如file1.c和
file2.c
)的,则在下面加上(注意红色字体的对应关系):
hello
-objs := file1.o file2.o ......
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules其中
-C $(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。
M=$(PWD)
指定了模块源代码的位置
modules目标指向obj-m变量中设定的模块。(8)insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。(9)Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块到处符号,则使用下面的宏:
EXPORT_SYMBOL(name);EXPORT_SYMBOL_GPL(name); |
#include <linux/init.h>#include <linux/module.h> |
MODULE_LICENSE("Dual BSD/GPL"); |
MODULE_AUTHOR("");MODULE_DESCRIPTION("");MODULE_VERSION("");MODULE_ALIAS("");MODULE_DEVICE_TABLE(""); |
MODULE_声明习惯上放在文件最后。(12)初始化和关闭初始化的实际定义通常如下:
static int _ _ init initialization_function(void){/*初始化代码*/}module_init(initialization_function) |
static int _ _exit cleanup_function(void){/*清除代码*/}module_exit(cleanup_function) |
struct something *item1;struct somethingelse *item2;int stuff_ok; void my_cleanup(void){ if (item1) release_thing(item1); if (item2) release_thing2(item2); if (stuff_ok) unregister_stuff(); return;}int __init my_init(void){ int err = -ENOMEM; item1 = allocate_thing(arguments); item2 = allocate_thing2(arguments2); if (!item2 || !item2) goto fail; err = register_stuff(item1, item2); if (!err) stuff_ok = 1; else goto fail; return 0; /* success */ fail: my_cleanup( ); return err;} |
#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>MODULE_LICENSE("Dual BSD/GPL");static char *whom = "Tekkaman Ninja";static int howmany = 1;static int TNparam[] = {1,2,3,4};static int TNparam_nr = 4;module_param(howmany, int, S_IRUGO);module_param(whom, charp, S_IRUGO);module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);static int hello_init(void){ int i; for (i = 0; i < howmany; i++) printk(KERN_ALERT "(%d) Hello, %s !/n", i, whom); for (i = 0; i < 8; i++) printk(KERN_ALERT "TNparam[%d] : %d /n", i, TNparam[i]); return 0;}static void hello_exit(void){ printk(KERN_ALERT "Goodbye, Tekkaman Ninja !/n Love Linux !Love ARM ! Love KeKe !/n");}module_init(hello_init);module_exit(hello_exit); |
[Tekkaman2440@SBC2440V4]#cd /lib/modules/[Tekkaman2440@SBC2440V4]#lscs89x0.ko hello.ko prism2_usb.kohello-param.ko p80211.ko[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1(0) Hello, KeKe !(1) Hello, KeKe !TNparam[0] : 4TNparam[1] : 3TNparam[2] : 2TNparam[3] : 1TNparam[4] : 1836543848TNparam[5] : 7958113TNparam[6] : 1836017783TNparam[7] : 0[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,6,7,8TNparam: can only take 4 argumentshello_param: `4' invalid for parameter `TNparam'insmod: cannot insert 'hello-param.ko': Invalid parameters (-1): Invalid argument[Tekkaman2440@SBC2440V4]# |
module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);”中
&TNparam_nr对输入参数数目的限***用。经过我的实验,表明
&TNparam_nr并没有对输入参数的数目起到限***用。真正起到限***用的是“
static int TNparam[] = {1,2,3,4};”本身定义的大小,我将程序进行修改:static int TNparam[] = {1,2,3,4}; 改为 static int TNparam[] = {1,2,3,4,5,6,7,8};其他都不变。编译后再进行实验,其结果是:
[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,6,7,8(0) Hello, KeKe !(1) Hello, KeKe !TNparam[0] : 4TNparam[1] : 3TNparam[2] : 2TNparam[3] : 1TNparam[4] : 5TNparam[5] : 6TNparam[6] : 7TNparam[7] : 8[Tekkaman2440@SBC2440V4]# |
linux/sched.h>” 最重要的头文件之一。包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明。(16)“#include <
linux/version.h>” 包含所构造内核版本信息的头文件。在学习过程中找到了几篇很好的参考文档:(1)第一章 模块(Modules) URL:http://greenlinux.blogcn.com/diary,103232026.shtml(2)《从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响》URL:http://www.ibm.com/developerworks/cn/linux/l-module26/(3)《Linux2.6内核驱动移植参考》 URL:http://blog.chinaunix.net/u1/40912/showart_377391.html
相关文章推荐
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序学习(0) -设备驱动介绍& Hello, world!模块
- .Linux设备驱动程序学习(0)——设备驱动介绍& Hello, world!模块 内核参数传递
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序学习(0) -设备驱动介绍& Hello, world!模块
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序学习(0)-Hello, world!模块
- Linux设备驱动程序,个人学习,第一个模块hello world
- linux设备驱动程序的hello模块编译过程
- Linux设备驱动程序学习(0)-Hello, world模块
- Linux设备驱动程序学习(2)-构造和运行模块
- Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)
- Linux设备驱动程序学习(18)-USB 驱动程序(二)
- Linux设备驱动程序学习笔记 高级字符驱动学习--阻塞型I/0
- Linux设备驱动程序学习-与硬件通信
- Linux设备驱动程序学习(2)-调试技术(补充)
- Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)
- Linux设备驱动程序学习(4)-高级字符驱动程序操作[(1)ioctl and llseek]