linux内核与驱动基于TE2440II开发板(一)构造和运行模块
哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处
http://blog.****.net/woshixingaaa/archive/2010/11/08/5995799.aspx
这个是《LINUX设备驱动程序》第二章的示例程序。编译这个程序之前要构建好内核源码树,
所谓源码树就是编译好内核后的/usr/src/$(shell uname -r) 目录
方法见我的博客http://blog.****.net/woshixingaaa/archive/2010/11/07/5993648.aspx
我的这个程序是在源码树之外编译内核模块。
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); static int __init hello_init(void){ printk(KERN_ALERT "hello world/n"); return 0; } static void __exit hello_exit(void){ printk(KERN_ALERT "goodbye cruel world/n"); } module_init(hello_init); module_exit(hello_exit);
module.h包含有可装载模块需要的大量符号和函数定义,包含init.h的目的是指定初始化和清除
函数。hello_init()和hello_exit()声明为静态是因为这两个函数只在本文件中使用。__init表明该函
数只在调用时被使用,__exit表示该函数只在模块被卸载或者系统关闭时被调用。printk是在内
核中运行向控制台输出显示的函数,LINUX内核首先在内核空间分配一个静态的缓冲区,作为
显示用的空间,然后调用sprintf,格式化显示字符串,然后调用tty_write向终端进行信息的显示。
module_init()和module_exit()是两个宏,前者用来注册模块,后者用来注销模块。使用insmod时
module_init()注册的函数被调用,使用rmmod时module_exit()注册的函数被调用。KERN_ALERT
用来定义这条消息的优先级,我们需要在模块中显示指定高优先级的原因在于:具有默认优先级
的消息可能不会输出在控制台上,这依赖于内核版本。
ifneq ($(KERNELRELEASE),)
obj-m:= hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clear:
rm -rf *.o
.PHONY: modules modules_install clear
endif
这个是Makefile文件,KERNELRELEASE是源码树顶层Makefile中定义的,ifneq用来判断参数是
否不相等,注意ifneq和后面的括号之间有一个空格,否则报错“Makefile:1: *** 遗漏分隔符 。 停止
。”将 KERNELRELEASE与空进行比较,如果不为空则指定目标obj-m:=hello.o, KERNELDIR 后
的?=是条件赋值运算符,如果KERNELDIR没有被赋值,则对它进行复制,后面这个/lib/modules/
$(shell uname -r)/build是一个符号链接,指向源码树。$(MAKE) -C $(KERNELDIR) M=$(PWD) mo
dules,我分析了好久,查了很多资料,是我感觉最难理解的一句。此命令首先改变当前工作目录到
-C指定的位置(即内核源码目录),其中保存有内核的顶层Makefile,执行此Makefile,需要编译模块,M
=$(PWD)指定了需要回到源码的目录,此时再次进入源码目录的Makefile,此时KERNELRELEASE
已被定义了,所以执行obj-m:=hello.o,指定了需要编译的模块,然后make modules进行编译。.PHON
Y: modules modules_install clear指定modules modules_install clear 都是伪指令,他们没有目标文件,
只相当于地址标号,如果有同名的目标不会发生错误。
可以安装模块了:
insmod hello.ko
如果在文本控制台下可以直接输出
hello world
如果是在终端的仿真器中则许查看日志
cat /var/log/syslog
此句不同发行版不同,有的是
cat /var/log/messages
然后卸载
rmmod hello
再在日志中查看
cat /var/log/syslog googbye cruel world
模块参数传递
内核中使用module_param宏来声明参数,这个宏定义在moduleparam.h中,module_param需要3个参数:变量的名字,类型,以及用于sysfs入口项的访问许可掩码。
在程序中加入
static char *whom = "world"; static int howmany = 1; module_param(howmany,1,S_IRUGO); module_param(whom,charp,S_IRUGO);
修改hello_init
static int hello_init(void) { int i; for (i = 0; i < howmany; i++) printk(KERN_ALERT "(%d) Hello, %s/n", i, whom); return 0; }
终端输入
sudo insmod hello.ko howmany=10 whom="Mom"