模块是内核执行的代码,与应用也没有什么本质的差别。
只不过它有它的书写规范
下面看一个简单的hello world 级别module 代码。
--------------------------------------------------------------------------------
[root@hjj /home/samba/test]# cat test.c
--------------------------------------------------------------------------------
#include <linux/module.h>
int mod_entry(void)
{
unsigned long d = be64_to_cpu(0x12345678abcdef);
printk("d is 0x%lx\n", d);
return 0;
}
void mod_exit(void)
{
printk("bye bye! module.\n");
}
module_init(mod_entry);
module_exit(mod_exit);
MODULE_AUTHOR("hjjdebug");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("test only");
格式定义了入口,出口以及其它辅助信息。
下面看看怎样把它编译成module.
--------------------------------------------------------------------------------
[root@hjj /home/samba/test]# cat Makefile
--------------------------------------------------------------------------------
$(warning KERNELRELEASE = $(KERNELRELEASE))
ifneq ($(KERNELRELEASE),)
obj-m := test.o
else
PWD=$(shell pwd)
KVER=$(shell uname -r)
KDIR=/lib/modules/$(KVER)/build
all:
make -C $(KDIR) M=$(PWD)
clean:
rm *.o *.ko modules.* Module.symvers test.mod.c
endif
[root@hjj /home/samba/test]#
makefile 写得如此奇怪,到底是什么意思呢? 下面先看看它的执行过程
--------------------------------------------------------------------------------
[root@hjj /home/samba/test]# make
--------------------------------------------------------------------------------
Makefile:1: KERNELRELEASE =
make -C /lib/modules/3.10.17/build M=/home/samba/test
make[1]: Entering directory `/usr/src/linux-3.10.17'
/home/samba/test/Makefile:1: KERNELRELEASE = 3.10.17
LD /home/samba/test/built-in.o
CC [M] /home/samba/test/test.o
Building modules, stage 2.
/home/samba/test/Makefile:1: KERNELRELEASE = 3.10.17
MODPOST 1 modules
CC /home/samba/test/test.mod.o
LD [M] /home/samba/test/test.ko
make[1]: Leaving directory `/usr/src/linux-3.10.17'
source Makefile, 第一遍KERNELRELEASE 为空,执行all 目标,将进入kernel 目录进行编译,
并指定了M 宏定义。
第2遍source Makefile, KERNELRELEASE 已经赋值,所以它读走了obj-m.
开始编译出test.o. 以上过程叫第一阶段,与普通make差别不大。
编译模块第2阶段。叫module 后处理阶段。由test.mod.c编译出test.mod.o
其中test.mod.c 是第一阶段生成的中间产物。是每一个.o 文件对应的头文件部分。
然后把头.o和体.o 连接成.ko
--------------------------------------------------------------------------------
模块测试,使用
--------------------------------------------------------------------------------
insmod, rmmod, modinfo, modprobe 来安装,卸载,查看模块
dmesg 可以查看模块中printk 信息输出。
--------------------------------------------------------------------------------
补充一下其它基本基础知识: 插入删除模块,创建设备节点,printk的使用.
--------------------------------------------------------------------------------
#### 1.模块命令:
lsmod, 与cat /proc/modules 内容一致.
insmod <filename>, 会在/sys/modules 目录下建立对应模块目录
insmod 不成功, device or resource busy 错误. 可能是设备号冲突.
用 cat /proc/devices 查看已有的设备号, 通过修改自己的主设备号,插入成功.
insmod 不成功, file exist 错误, 重复插入,需先删除模块
rmmod <modname>
modinfo <filename>
安装模块后要关注它的主设备号(通过 /proc/devices 查看),以便创建设备节点
#### 2. 创建设备节点
安装了模块,获得了设备号, 如果你想使用设备,需要在/dev下建立设备节点,可以用mknod 创建
例如:
sudo mknod /dev/myTimer c 200 0
会创建/dev/myTimer 节点, 字符设备,主设备号200,从设备号0
这样通过访问设备文件来操作驱动.
#### 3. 对printk 控制台log 级别控制
$ cat /proc/sys/kernel/printk
4 4 1 7
这4个数是什么意思?
前言:
printk log 级别一共8个 0 - 7, 数值越低,越紧急.
printk 的用法一般是 printk("级别号msg"), 级别号就是0-7
如果忽略级别号,直接使用 printk("msg"), 那就使用默认级别打印. 你可以修改默认级别
下面说说4个数含义:
(1) 控制台日志门槛:优先级高于该值的消息将被打印至控制台。
(2) 默认的消息级别:省略级别号的打印按该级别打印
(3) 最低的控制台日志级别:你不可能设置的控制台门槛比这个还低
(4) 默认的控制台级别:
$ echo 8>/proc/sys/kernel/printk
$ cat /proc/sys/kernel/printk
8 4 1 7
printk 控制台门槛变成了8, 这样所有的printk 信息都会打印到控制台.不必通过dmesg 看了.
$ echo "8 8 8 8">/proc/sys/kernel/printk
$ cat /proc/sys/kernel/printk
8 8 8 8
说了这么多,其实我们无非是想把自己的消息打印到控制台方便查看!
但是在ubuntu 系统上虚拟终端里却做不到(到ubuntu18还有这个问题)
就是终端是 /dev/pts 类型的, 把打印级别提高到0也不会向控制台输出, cat /proc/kmsg 也不太听话.
唯一好用的是dmesg.
但真实终端没有这些问题, 真实终端就是ctrl-alt-Fn 打开的纯字符终端, 名字是 /dev/ttyx 类型的.
但是真实终端也有不爽的问题, 字体是系统字体,不可设置,太小了. 字符向图形切换还经常容易死机.
所以还是放弃了printk 直接向控制台输出.
继续选择用虚拟终端通过dmesg 来查看printk 信息, 也可以监视/proc/kmsg
~
40,4 Bot