首先内核编程从一个hello开始
1 /*********************************************************************************
2 * Copyright: (C) 2013 Guo Wenxue<guowenxue@gmail.com>
3 * All rights reserved.
4 *
5 * Filename: kernel_hello.c
6 * Description: This file is the linux kernel module sample
7 *
8 * Version: 1.0.0(03/16/2013~)
9 * Author: Guo Wenxue <guowenxue@gmail.com>
10 * ChangeLog: 1, Release initial version on "03/16/2013 10:50:26 AM"
11 *
12 ********************************************************************************/
13 #include <linux/init.h>
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16
17 static __init int hello_init(void)
18 {
19 printk(KERN_ALERT "Hello, LingYun IoT Studio!\n");
20 return 0;
21 }
22
23 static __exit void hello_exit(void)
24 {
25 printk(KERN_ALERT "Goodbye, I have found a good job!\n");
26 }
27
28 module_init(hello_init);
29 module_exit(hello_exit);
30 MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");
31 MODULE_DESCRIPTION("Linux Kernel hello module (C) LingYun IoT Studio");
32 MODULE_LICENSE("Dual BSD/GPL");
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
这三个内核头文件时内核编译时必须带的。
__init __exit属性:
#define __init __attribute__ ((__section__ (".init.text"))) __cold
#define __exit __attribute__ (( __section__ (".exit.text"))) __cold
模块初始化的时候不同的属性函数会被放入目标文件如.init.text中,这一过程是通过编译内核时为相关目标平台提供了xxx.lds链接脚本来指导ld完成的。 对编译成module的代码和数据来说,当模块加载时,__init属性的函数就被执行;当模块去除时,__exit属性的函数就会被执行。
module_init module_exit:
就是一个宏定义,分别用来注册模块在内核的初始化和退出。详细的定义很复杂,他有很多层的定义module_init(fn)—> __initcall(fn) —> device_initcall(fn) —> __define_initcall(fn, 6)
MODULE_LICENSE宏的说明:
从2.4.10版本内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,会收到内核被污染 “kernel tainted” 的警告。从linux/module.h文件中可以看到,被内核接受的有意义的许可证有 “GPL”,“GPL v2”,“GPL and additional rights”,“Dual BSD/GPL”,“Dual MPL/GPL”,“Proprietary”。
打印函数printk:
printk 函数在 Linux 内核中定义并且对模块可用,它与标准 C 库函数 printf 的行为相似。内核需要它自己的打印函数, 因为它靠自己运行, 没有 C 库的帮助. 模块能够调用 printk 是因为在 insmod加载了它之后, 模块被链接到内核并且可存取内核的公用符号. 字串 KERN_ALERT 是消息的优先级。printk支持分级别打印调试,这些级别定义在linux-3.0/include/linux/printk.h文件中:
#define KERN_EMERG “<0>” /* system is unusable */
#define KERN_ALERT “<1>” /* action must be taken immediately */
#define KERN_CRIT “<2>” /* critical conditions */
#define KERN_ERR “<3>” /* error conditions */
#define KERN_WARNING “<4>” /* warning conditions */
#define KERN_NOTICE “<5>” /* normal but significant condition */
#define KERN_INFO “<6>” /* informational */
#define KERN_DEBUG “<7>” /* debug-level messages */
/* Use the default kernel loglevel */
#define KERN_DEFAULT “<d>”
Linux内核中printk()的语句是否打印到串口终端上,与u-boot里的bootargs参数中的 loglelve=7相关,只有低于loglevel级别的信息才会打印到控制终端上,否则不会在控制终端上输出。这时我们只能通过dmesg命令查看。 linux下的dmesg命令的可以查看linux内核所有的打印信息,它们记录在/var/log/messages系统日志文件中。linux内核中的打印信息很多,我们可以使用 dmesg -c命令清除之前的打印信息。
写一个makefile
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
2 PWD := $(shell pwd)
3 obj-m := kernel_hello.o
4
5 modules:
6 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
7 @make clear
8
9 clear:
10 @rm -f *.o *.cmd *.mod.c
11 @rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
12 @rm -f .*ko.cmd .*.o.cmd .*.o.d
13 @rm -f *.unsigned
14
15 clean:
16 @rm -f hello.ko
刚好学习驱动时候把makefile学习一下
KERNEL_DIR、PWD是变量,这里表示内核路径和当前目录路径
obj-m是把指定代码编译成模块,与其对应的是obj-y。
modules就是目标 动作-C在指定路径下去编译并在M=指定的路径下生成相应的文件,然后删除相关文件,clear、clean是伪目标,执行伪目标是,敲make {伪目标名}。
这里写make一直错误,一只编译显示遗漏分隔符,然后又找不到原因,的确是一个tab的距离,然后检查了好多遍,真的是弱小又无助
最后解决了就是,发现是一个tab是四个空格组成的,这样即使一个tab键跳四格,实质还是空格,然后在vimrc文件中配置autocmd FileType make set noexpandtab这句话就好了,就是一个宽度为四的空格。
make之后就会产生我们的.ko文件,也就是内核模块文件
关于内核的操作都需要sudo权限
dmesg查看Linux内核的打印信息,dmesg -c将会清除之前Linux内核的打印信息
insmod是用来安装模块的,所以sudo insmod kernel_hello.ko就可以安装我们的hello模块了
xiaobaicai@xiaobaicai:~/fl2440/driver/x86$ sudo insmod kernel_hello.ko
xiaobaicai@xiaobaicai:~/fl2440/driver/x86$ dmesg
[521924.615161] Hello, LingYun IoT Studio!
xiaobaicai@xiaobaicai:~/fl2440/driver/x86$ sudo rmmod kernel_hello
xiaobaicai@xiaobaicai:~/fl2440/driver/x86$ dmesg
[521924.615161] Hello, LingYun IoT Studio!
[521951.191626] Goodbye, I have found a good job!
安装模块时初始化代码执行打印 Hello, LingYun IoT Studio!
卸载模块时卸载代码执行打印Goodbye, I have found a good job!
然后我们在我们的arm开发板上测试一下这个驱动
另写一个makefile用我们的交叉编译器来编译,而且他依赖我们内核源码路径,内核一定是已经编译过的,因为模块之间相互依赖。
LINUX_SRC = ${shell pwd}/../linux/linux-3.0/
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linuxINST_PATH=/tftp
PWD := $(shell pwd)
EXTRA_CFLAGS+=-DMODULE
obj-m += kernel_hello.o
modules:
@make -C $(LINUX_SRC) M=$(PWD) modules
@make clear
uninstall:
rm -f ${INST_PATH}/*.ko
install: uninstall
cp -af *.ko ${INST_PATH}
clear:
@rm -f *.o *.cmd *.mod.c
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
@rm -f .*ko.cmd .*.o.cmd .*.o.d
clean: clear
@rm -f *.ko
编译完成后通过windows转发到开发板上。
~ >: tftp -gr kernel_hello.ko 192.168.1.251
kernel_hello.ko 100% |*******************************| 23706 0:00:00 ETA
~ >: ls
: etc linuxrc sys
apps info mnt tmp
bin init proc usr
data kernel_hello.ko root var
dev lib sbin
~ >: insmod kernel_hello.ko
Hello, LingYun IoT Studio!
~ >: lsmod
kernel_hello 561 0 - Live 0xbf000000
~ >: rmmod kernel_hello
Goodbye, I have found a good job!
开发板测试没有问题。