编写内核模块

  内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),咱们简称为模块。Linux内核之因此提供模块机制,是由于它自己是一个单内核(monolithic kernel)。单内核的最大优势是效率高,由于全部的内容都集成在一块儿,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。不少驱动程序都以模块的形式存在,用户能够有选择的加载须要的驱动程序。html

 

1、 什么是模块linux

  模块是具备独立功能的程序,它能够被单独编译,但不能独立运行。它在运行时被连接到内核做为内核的一部分在内核空间运行,这与运行在用户空间的进程是不一样的。模块一般由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其余内核上层的功能。shell

 

2、 编写一个简单的模块编程

  模块和内核都在内核空间运行,模块编程在必定意义上说就是内核编程。由于内核版本的每次变化,其中的某些函数名也会相应地发生变化,所以模块编程与内核版本密切相关。如下例子针对2.6内核:数据结构

  tasklet.c文件函数

 1 #include <linux/module.h>
 2 #include <linux/init.h>
 3 #include <linux/kernel.h>
 4 #include <linux/interrupt.h>  
 5 
 6 static struct tasklet_struct my_tasklet;  
 7 
 8 static void tasklet_handler (unsigned long data)
 9 {
10         printk(KERN_ALERT "tasklet_handler is running.\n");
11 }  
12 
13 static int __init test_init(void)
14 {
15         tasklet_init(&my_tasklet, tasklet_handler, 0);
16         tasklet_schedule(&my_tasklet);
17         return 0;
18 }  
19 
20 static void __exit test_exit(void)
21 {
22         tasklet_kill(&my_tasklet);
23         printk(KERN_ALERT "test_exit running.\n");
24 }
25 MODULE_LICENSE("GPL");  
26 
27 module_init(test_init);
28 module_exit(test_exit);

  1.头文件说明:全部模块都要使用头文件module.h,此文件必须包含进来;头文件kernel.h包含了经常使用的内核函数;头文件init.h包含了宏_init和_exit,它们容许释放内核占用的内存。interrupt.h文件时编写tasklet(关于tasklet的说明能够参考linux中断与异常)必需要用的头文件。spa

  2.test_init是模块的初始化函数,它必需包含诸如要编译的代码、初始化数据结构等内容,其中的tasklet_init初始化咱们本身建立的tasklet,tasklet_schedule函数则是将咱们初始化后的tasklet挂到tasklet链表中,并激活软中断来执行咱们的tasklet。日志

  3.test_exit是模块的退出和清理函数。此处能够作全部终止该内核模块时相关的清理工做。code

  4.函数module_init()和module_exit()是模块编程中最基本也是必须的两个函数。module_init()向内核注册模块,并调用注册作模块的初始化工做的函数,而module_exit()注销模块并注册模块注销时执行的清理函数htm

  5.第10行使用了printk()函数,该函数是由内核定义的,功能与C库中的printf()相似,它把要打印的信息输出到终端或系统日志。字符串前面的KERN_ALERT是输出的级别,表示当即在终端输出,能够调用dmesg命令来查看输出。

  有了上面的各个组成,一个模块就算编写好了。

 

3、 编译模块

  编译模块不想编译普通的c文件,直接敲几个命令就能够了,编译模块必须用Makefile文件,并且Makefile文件必须按必定的规则编写,这个和平时编写的Makefile文件不同。编写内核模块的Makefile文件内容几乎相同的。下面是模版:

obj-m += tasklet.o        # 产生tasklet 模块的目标文件
CURRENT_PATH := $(shell pwd)   #模块所在的当前路径
LINUX_KERNEL := $(shell uname -r)    #Linux内核源代码的当前版本
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) #Linux内核源代码的绝对路径
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules   #编译模块了
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean    #清理

  除了第一行要把tasklet换成编写的目标模块的.c文件名外,其它都是同样的。

  一切都准备好以后,直接make一下就ok了。

 

4、 运行模块

  运行模块也不能像运行普通程序同样,直接./tasklet来运行,必须经过专门的命令来运行。

  使用$insmod tasklet.ko命令直接将咱们的模块插入到内核中,模块被插入内核时就会调用咱们注册的初始化函数test_init,用dmesg命令查看输出,能够看到tasklet_handler is running.消息被打印了。

  使用$lsmod命令来查看系统中的模块:

  使用$rmmod tasklet来卸载模块,卸载模块的同时会执行注册函数test_exit,用dmesg能够查看打印消息。

相关文章
相关标签/搜索