Linux 内核模块编程一

时间:2021-05-27 15:45:01

通过一些简单的例子来学习linux内核模块

/*  
* hello-1.c - The simplest kernel module.
*/
#include <linux/module.h>/* Needed by all modules */
#include <linux/kernel.h>/* Needed for KERN_INFO */

int init_module(void)
{
printk(KERN_INFO "Hello world 1.\n");

/*
* A non 0 return means init_module failed; module can't be loaded.
*/
return 0;
}

void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}

内核模块必须至少有两个函数:初始化init_module(),当一个内核模块被加载到内核的时候调用该函数;去初始化函数cleanup_module(),当一个内核模块被卸载的时候调用该函数,在linux 内核2.3.13之前内核模块的初始化和去初始化函数必须使用init_module()和cleanup_module()的函数名,不过在之后的版本只要符合函数命名规则你可以使用任何你想用的函数名。但是由于习惯开发人员还是一直使用init_module()和cleanup_module()作为初始化和去初始化函数。


printk()是一个内核的一个记录日志的机制,经常用来记录信息或者警告。printk可以指定输出日志的优先级,在include/linux/kern_levels.h中有相应的宏定义,所以在指定优先级的时候不要使用魔鬼数字

#define KERN_SOH"\001"/* ASCII Start Of Header */
#define KERN_SOH_ASCII'\001'

#define KERN_EMERGKERN_SOH "0"/* system is unusable */
#define KERN_ALERTKERN_SOH "1"/* action must be taken immediately */
#define KERN_CRITKERN_SOH "2"/* critical conditions */
#define KERN_ERRKERN_SOH "3"/* error conditions */
#define KERN_WARNINGKERN_SOH "4"/* warning conditions */
#define KERN_NOTICEKERN_SOH "5"/* normal but significant condition */
#define KERN_INFOKERN_SOH "6"/* informational */
#define KERN_DEBUGKERN_SOH "7"/* debug-level messages */

#define KERN_DEFAULTKERN_SOH "d"/* the default kernel loglevel */

/*
* Annotation for a "continued" line of log printout (only done after a
* line that had no enclosing \n). Only to be used by core/arch code
* during early bootup (a continued line is not SMP-safe otherwise).
*/
#define KERN_CONT""

如果不指定优先级,这printk就使用默认的优先级,DEFAULT_MESSAGE_LOGLEVEL 在linux-3.6.10/kernel/printk.c中有定义

如果printk指定的优先级没有console_loglevel高的话,那么信息就不会在你所使用的终端打印出来。如果syslogd和klogd服务进程运行的话,无论是否在终端打印,这些信息都将会被记录到/var/log/messages

/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL

/* We show everything that is MORE important than this.. */
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */

如果未对CONFIG_DEFAULT_MESSAGE_LOGLEVEL进行配置,则默认值是4

[root@localhost /]# grep -r "CONFIG_DEFAULT_MESSAGE_LOGLEVEL" /lib/modules/3.6.10-4.fc18.i686/build/
/lib/modules/3.6.10-4.fc18.i686/build/include/config/auto.conf:CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
/lib/modules/3.6.10-4.fc18.i686/build/include/generated/autoconf.h:#define CONFIG_DEFAULT_MESSAGE_LOGLEVEL 4
/lib/modules/3.6.10-4.fc18.i686/build/.config:CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4


好了,编写完上边的小测试程序之后,我就开始编译验证它吧

编写这个程序的Makefile

obj-m += hello-1.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

这里简单解释一下linux 3.6.10内核的Makefile

The Makefiles have five parts:
Makefile the top Makefile.
.config the kernel configuration file.
arch/$(ARCH)/Makefile the arch Makefile.
scripts/Makefile.* common rules etc. for all kbuild Makefiles.
kbuild Makefiles there are about 500 of these.

1、顶层Makefile读取.config内核配置文件,顶层Makefile负责编译两个主要的镜像文件:vmlinux(驻留内核镜像)和 内核模块,它通过递归便利内核源码树的子目录来编译这些目标文件,访问的子目录列表取决于内核的配置。

2、顶层Makefile还会包含arch/$(ARCH)/Makefile,这些平台Makefile向顶层Makefile提供架构特性信息

3、每一个子目录下有一个kbulid Makefile会展开从上面传下来的命令,kbulid Makefile利用来自.config的配置信息通过kbulid构建各种不同的文件编译内置或是模块可加载的目标

4、scripts/Makefile.* 包含了所有的定义、规则等等,他们在kbulid makefiles的基础上构建内核


第一行 obj-m += hello-1.o 是kbuild Makefile中的目标定义部分,同时目标定义部分也是kbuild Makefile的重要组成部分。该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c 或foo.S文件编译得到,它将会编译成一个可加载的模块而不是直接编译进内核

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

是到指定路径/lib/moudles/$(shell uname -r)/build下执行make,后面是传入的参数

make M=dir clean     Delete all automatically generated files
make M=dir modules   Make all modules in specified dir



在代码目录执行make

[root@localhost modules_programming]# make
make -C /lib/modules/3.6.10-4.fc18.i686/build M=/myfile/modules_programming modules
make[1]: Entering directory `/usr/src/kernels/3.6.10-4.fc18.i686'
  CC [M]  /myfile/modules_programming/hello-1.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /myfile/modules_programming/hello-1.mod.o
  LD [M]  /myfile/modules_programming/hello-1.ko
make[1]: Leaving directory `/usr/src/kernels/3.6.10-4.fc18.i686'

[root@localhost modules_programming]# ls
hello-1.c   hello-1.mod.c  hello-1.o  modules.order
hello-1.ko  hello-1.mod.o  Makefile   Module.symvers

然后再linux终端下执行insmod rmmod命令如下:

Linux 内核模块编程一

在执行insmod命令打印hello world是因为我把printk的级别改成了KERN_ERR

如果是KERN_INFO是不会在终端打印的只会在/var/log/messages中记录

注:如果是在远程登录的终端上执行加载命令,只有printk的优先级为KERN_EMERG

才会在终端打印出信息


通过lsmod命令查看加载的模块

[root@localhost modules_programming]# lsmod |grep hello
hello_1                12395  0

通过modinfo 命令查看模块的信息

[root@localhost modules_programming]# modinfo hello-1.ko
filename:       /myfile/modules_programming/hello-1.ko
depends:
vermagic:       3.6.10-4.fc18.i686 SMP mod_unload 686


使用modprobe命令加载模块

[root@localhost modules_programming]# modprobe hello-1

FATAL: Module hello-1 not found.

modprobe expects an up-to-date modules.dep.bin file (or fallback human
       readable modules.dep file), as generated by the corresponding depmod
       utility shipped along with modprobe (see depmod(8)). This file lists
       what other modules each module needs (if any), and modprobe uses this
       to add or remove these dependencies automatically.

modprobe会根据depmod所产生的相依关系,决定要载入哪些模块

[root@localhost modules_programming]# depmod -v /myfile/modules_programming/hello-1.ko

[root@localhost modules_programming]# modprobe -v hello-1
insmod /myfile/modules_programming/hello-1.ko

modprobe实际也是调用insmod去加载模块,只不过它会找到该模块所依赖的其他模块也一并装载


在编写内核的过程中如果没有加LICENSE就会在/var/log/messages

中打印下面的warnning信息

[root@localhost modules_programming]# dmesg |tail -10
[   29.743705] nf_conntrack: automatic helper assignment is deprecated and it will be removed soon. Use the iptables CT target to attach helpers instead.
[ 3751.191888] hello_1: module license 'unspecified' taints kernel.
[ 3751.192068] Disabling lock debugging due to kernel taint