1.linux源代码结构 fs:file system文件系统,usr/src/linux:linux内核头文件,mm:内存管理代码
2.下载linux内核代码 可自己选择版本,这里是比较老的版本适合小白入手(eg:我用的是0.1x,点击直接下载即可) http://oldlinux.org/Linux.old/kernel/
3.Linux系统的整体结构
4.可加载的内核模块,就是linux使用C语言写的,由于C语言是模块化编程的,添加模块很麻烦,所以为了给Linux添加模块,于是诞生了LKM,就是可以使使用户随时添加内核模块到内核,也可以随时卸下,不和内核建立捆绑,我们今天编写的内核程序就是基于这个原理的.
5.内核模块的编写,简单例子,只使用一个内核函数printk();printk在内核源码中用来记录日志信息的函数,只能在内核源码范围内使用。用法和printf非常相似
创建k.c文件命令:sudo vim k.c (sudo是切换到root用户,如果你已经是root了,就不用sudo了),另外请注意,内核模块只能root运行,普通用户无法让内核模块运行,
所以,得切换到root模式,su - root,但是在root下操作要小心啊!!!
//k.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
//module.h包含了对模块的版本控制
//kernel.h包含了常用的内核函数
//init.h包含了宏__init和__exit
/*
* 模块的初始化函数 lkp_init()
* __init是用于初始化的修饰符
*/
static int __init lkp_init(void)
{
//<1>是输出的级别,表示立即在终端输出
printk("<1>hello,Kernal!...\\n!");
return 0;
}
/*
* 模块的退出和清理函数 lkp_exit()
*/
static void __exit lkp_exit(void)
{
printk("<1>Goodbye,world!...\\n");
}
module_init(lkp_init);
module_exit(lkp_exit);
/*
* 模块的许可证声明GPL
*/
MODULE_LICENSE("GPL");
补充知识点:
printk相比printf来说还多了个:日志级别的设置,用来控制printk打印的这条信息是否在终端上显示的,当日志级别的数值小于控制台级别时,printk要打印的信息才会在控制台打印出来,否则不会显示在控制台!内核中有个8个级别.
#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 */
6.Makefile文件编写,Makefile文件和k.c文件放在同一个目录下 创建Makefile文件命令:sudo vim Makefile
引入原因之1:使用GCC的命令进行程序编译时,当程序是单个文件时编译是比较方便的,但当工程中的文件数目增多,甚至非常庞大,并且目录结构关系复杂时,便需要通过makefile来进行程序的编译
obj-m :=k.o #产生k模块的目标文件,这里的k必须和c语言的文件名对应,后缀不同,这里使用了一点shell脚本的知识,读者请百度自行参考
CURRENT_PATH := $(shell pwd) #模块当前所在的当前路径
LINUX_KERNEL := $(shell uname -r) #linux内核源代码的当前版本,uame -r 查看内核的linux命令
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
#linux内核源代码的绝对路径
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块,Makefile文件中若一行是命令,必须以tab键开头
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理模块
补充知识:
obj-m := *.o
obj-y := *.o
上面两者的区别在于,前者才会生成ko文件,后者只是代码编译进内核,并不生成ko文件。
7.编译,使用make命令
8.查看运行结果及常用命令 注意:在强调一下,root模式下才能运行
insmod 模块名 #将模块插入内核中,eg:insmod k.ko,把k.ko模块插入内核
rmmod 模块名 #将模块从内核中删去 eg:rmmod k.ko,将k.ko模块移出内核 o=哦,不是0
dmesg #查看模块信息的命令,在make之后,使用就能看到信息
lsmod #查看内核模块,可搭配grep使用,eg:lsmod | grep k 查找内核中的k模块
程序运行结果:
9.附加知识